PHP SoapClient port bug workaround
PHP’s SoapClient has a bug (PHP 5.2.10 here, still not fixed apparently) when the SOAP service must be accessed on a different port other than 80. The WSDL file is fetched correctly, but all subsequent requests are made without any port in the Host field. This causes a SoapFault exception when trying to call any of the service’s methods.
So if the WSDL location is:
http://example.com:33080/soap/server/path?WSDL
All requests after fetching the WSDL file will be made to:
http://example.com/soap/server/path
The simplest way i could work around this was to extend SoapClient and intercept the constructor and the __doRequest method to inject the port in the location on each request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <?php class My_SoapClient extends SoapClient { public function __construct($wsdl, $options) { $url = parse_url($wsdl); if ($url['port']) { $this->_port = $url['port']; } return parent::__construct($wsdl, $options); } public function __doRequest($request, $location, $action, $version) { $parts = parse_url($location); if ($this->_port) { $parts['port'] = $this->_port; } $location = $this->buildLocation($parts); $return = parent::__doRequest($request, $location, $action, $version); return $return; } public function buildLocation($parts = array()) { $location = ''; if (isset($parts['scheme'])) { $location .= $parts['scheme'].'://'; } if (isset($parts['user']) || isset($parts['pass'])) { $location .= $parts['user'].':'.$parts['pass'].'@'; } $location .= $parts['host']; if (isset($parts['port'])) { $location .= ':'.$parts['port']; } $location .= $parts['path']; if (isset($parts['query'])) { $location .= '?'.$parts['query']; } return $location; } } |
It works for me, and I would like to know if it doesn’t cover your particular case :)
Detectarea platformelor mobile in PHP
Folosind diverse resurse online (inclusiv lista de User-Agents folositi de dispozitivele mobile de aici), am scris o mica clasa PHP ce detecteaza platforma mobila a utilizatorului din cele mai cunoscute (fara nici o pretentie de exhaustivitate):
- Google Android
- BlackBerry
- iPhone
- Opera Mini
- Palm
- Windows Mobile
- Generic (adica restul)
Modul de utilizare este foarte simplu, descris in detaliu pe Google Code
Nu am avut foarte multe device-uri la indemana pentru teste (decat cateva standard, plus un iPhone si un Blackberry), asa ca as aprecia daca m-ati atentiona daca nu functioneaza cum ar trebui.
FTL jump … now!
Fiecare fisier JavaScript, CSS, imagine, Flash, etc. afisat utilizatorului reprezinta un request catre server. Pentru fiecare asemenea fisier browserul trimite cererea, serverul o preia, proceseaza, serveste fisierul cerut, si tot asa. La site-urile “mari” se simte. In load-ul serverului, in latimea de banda consumata, in experienta utilizatorilor.
Urmand recomandarile echipei de la Yahoo!, o sa va arat la ce solutie am ajuns.
Dar mai intai … cifre. Am ales Okazii.ro drept studiu de caz (toate masuratorile sunt facute cu YSlow, folosind o versiune salvata a primei pagini):
Fara FTL (pagina salvata aici)
- Nota generala in YSlow: E
- Numar request-uri pe prima pagina: 62
- Dimensiune prima pagina: 406.4KB
Cu FTL (pagina salvata aici)
- Nota generala in YSlow: C
- Numar request-uri pe prima pagina: 51
- Dimensiune prima pagina: 226.0KB
Calculat la traficul lor (aprox. 82.000 vizitatori unici pe zi), netinand cont de cache in nici unul din cazuri, asta se traduce in o diferenta de 902.000 de request-uri si 14.1 GB trafic lunar.
Cum se foloseste
Presupunand urmatoarea structura de directoare:
/public
/css
/js
index.phpDescarcati arhiva si extrageti fisierele in root asa incat noua structura va fi:
/ftl
/cache
/lib
CSS.php
File.php
JS.php
config.php
index_css.php
index_js.php
/public
/css
/js
index.phpMutati fisierele index_css.php si index_js.php din directorul /ftl in directorul /public/css, respectiv /public/js, si redenumiti-le in index.php:
/ftl
/cache
/lib
CSS.php
File.php
JS.php
config.php
/public
/css
index.php
/js
index.php
index.phpAdaugati liniile urmatoare in .htaccess:
1 2 3 4 5 6 7 8 | <IfModule mod_expires.c>
ExpiresActive On
ExpiresByType application/x-javascript A315360000
ExpiresByType text/css A315360000
</IfModule>
RewriteEngine On
RewriteRule ^public/(js|css)/([a-z0-9\.]+)\.(js|css)$ public/$1/index.php?hash=$2 |
Includeti fisierele necesare in index.php:
1 2 3 4 | <?php include("ftl/JS.php"); include("ftl/CSS.php"); ?> |
Totul e pregatit, tot ce mai trebuie sa facem e sa incarcam in pagina fisierele. Spre exemplu, ca sa incarc in <head> cateva fisiere CSS si JS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php $css = new FTL_CSS(); $js = new FTL_JS(); $css->add('reset') ->add('typo') ->add('layout') ->add('error') ->add('ie', 'lte IE 7'); $js->add('prototype') ->add('sizzle') ->add('effects') ->add('lightbox'); echo $css->render(); echo $js->render(); ?> |
Nu uitati sa setati drepturi de scriere pe subdirectoarele din directorul /ftl/cache.
Codul HTML generat va arata in felul urmator:
1 2 3 | <link rel="stylesheet" type="text/css" media="screen" href="public/css/1e9dd525d1fbe94f7b8aede22cc141f5.1242750618.css" /> <!--[if lte IE 7]><link rel="stylesheet" type="text/css" media="screen" href="public/css/25400724d7370b0b29c9369d9af3dd21.1242750626.css" /><![endif]--> <script type="text/javascript" src="public/js/56c728a5a7689ab1663359a7edff160a.1242751025.js"></script> |
Doua fisiere .css (unul pentru cele standard, unul incarcat doar pentru IE), si un fisier .js. 3 request-uri in loc de 9, si o dimensiune totala de aprox. 6.5 ori mai mica. Fisierele sunt atat minified cat si gzipped, folosind librariile CSSTidy, respectiv Packer
Suna bine, dar ce se intampla cand …
-
Modific sau sterg un fisier .css sau .js ? Trebuie sa schimb ceva de fiecare data ?
Nu. De fiecare data cand un fisier este modificat sau sters, fisierul rezultat este regenerat automat, fara nevoie de interventie.
-
Un utilizator foloseste un browser ce nu suporta Gzip (stone-age style) ?
Respectivului utilizator ii este servita automat o versiune doar minified. Putin mai mare, evident, dar functionala.
-
Structura directoarelor mele nu corespunde cu cea din exemplul de mai sus ?
In directorul /ftl este un fisier config.php unde puteti schimba caile catre fisiere / directoare. Aveti grija numai ca include-urile sa functioneze.
PHP Class: AutoLoader
Oricine a adoptat metodologia OOP in lucrul cu PHP cu siguranta s-a bucurat la implementarea functiei __autoload in PHP 5, ce permite incarcarea automata a fisierului corespunzator unei clase la momentul invocarii acesteia.
Probleme
In ciuda potentialului imens adus de aceasta functionalitate, majoritatea cartilor de specialitate prezinta doar cel mai elementar exemplu pentru exploatarea acestei functii, si anume simpla incarcare a fisierului cu numele identic sau asemanator cu cel al clasei necesare:
1 2 3 | function __autoload($class_name) { require_once $class_name . '.php'; } |
Imediat dupa trecerea entuziasmului, incepem sa observam minusurile acestei abordari:
- Necesitatea ca fisierele si clasele corespunzatoare sa fie denumite la fel.
- Toate fisierele vor trebui sa fie situate in acelasi director, pentru a putea fi incluse.
- Fiecare fisier va putea contine o singura clasa. (Cu toate ca si eu consider “best-practice” pastrarea cate unui singur fisier per clasa, prefer sa stiu ca nu sunt constrans in aceasta directie).
Cerinte
Una din marile frumuseti ale programarii este ca permite transpunerea logicii gandirii umane intr-un limbaj “inteles” de catre tehnologiile disponibile. La fel este si cazul procesului decizional ce urmeaza:
- Avem nevoie de un sistem de localizare si incarcare a claselor care sa fie independent de structura directoarelor aplicatiei, si care sa rezolve punctele enumerate mai sus.
- Va trebui ca solutia adoptata sa poata depista clasele in anumite directoare alese, in acest fel asigurandu-se incarcarea unui fisier necesar, din locatia deja cunoscuta a acestuia.
- Pentru a evita solicitarea excesiva a serverului, este necesar ca sistemul sa permita o memorare (cache) a locatiei fiecarei clase, asa incat parcurgerea directoarelor sa se faca o singura data.
Solutie
Dupa ce a trecut prin mai multe iteratii si versiuni (si probabil va mai trece), va invit sa descarcati si folositi versiunea actuala a clasei AutoLoader. Modul de utilizare este simplu, doar incarcati fisierul in care se afla aceasta, si folositi urmatoarea varianta a functiei __autoload:
1 2 3 4 5 6 7 8 9 | function __autoload($class) { $class_dirs = array("./"); // array ce va contine directoarele in care vom cauta (inclusiv subdirectoarele acestora, recursiv) $autoloader = new AutoLoader($class_dirs, 'autoloader-cache.php'); try { $autoloader->loadClass($class); } catch (Exception $e) { exit($e->getMessage()); } } |
Puteti vedea output-ul unui scurt exemplu aici, ce contine urmatorul cod PHP:
1 2 3 | include("AutoLoader.class.php"); include("autoload.func.php"); new Test; |
Avantaje
- Incarcarea explicita a unei singure clase, cea de autoload.
- Parcurgerea recursiva a directoarelor indicate si pastrarea locatiei fiecarei clase intr-un fisier de cache, pentru acces rapid.
- Regenerarea automata a acestui fisier pe parcursul dezvoltarii. In momentul adaugarii / modificartii unei clase, locatia acesteia negasindu-se in fisierul existent, acesta este recreat.
- Inexistenta restrictiei privitoare la legatura intre numele unei clase si denumirea fisierului in care aceasta se afla.
Dezavantaje
Unul din dezavantajele minore ale acestui sistem este imposibilitatea existentei a doua clase cu acelasi nume. In cazul in care numele a doua clase coincid, locatia primei intalnite la scanarea fisierelor va fi suprascrisa in fisierul de cache de catre cea de-a doua.
Cu toate ca in mod normal nu ar trebui sa existe doua clase denumite la fel, se impune in acest fel o atentie sporita in momentul utilizarilor de clase / plugin-uri “3rd party”, deoarece numele uneia din acestea poate coincide cu cel al unei clase deja existente.
Download
Puteti downloada atat clasa AutoLoader, cat si toate fisierele corespunzatoare exemplului anterior:
Download AutoLoader.class.php
Download exemplu
Ca de obicei, sunt foarte interesat de eventualele imbunatatiri / modificari pe care le-ati propune. De asemenea, daca depistati bug-uri, as fi recunoscator daca mi le-ati indica.
Sunt Victor Stanciu, web developer, si scriu despre dezvoltare, standarde, tehnici si tehnologii. (