diff --git a/.buildpath b/.buildpath new file mode 100644 index 0000000000000000000000000000000000000000..8bcb4b5fd612e3ad55fb07e4bed087c55afd0861 --- /dev/null +++ b/.buildpath @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<buildpath> + <buildpathentry kind="src" path=""/> + <buildpathentry kind="con" path="org.eclipse.php.core.LANGUAGE"/> +</buildpath> diff --git a/.project b/.project new file mode 100644 index 0000000000000000000000000000000000000000..fc09879368ec85178bdd683d878d157e52c96fca --- /dev/null +++ b/.project @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>batchval</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.wst.validation.validationbuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.dltk.core.scriptbuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.php.core.PHPNature</nature> + </natures> +</projectDescription> diff --git a/lib/.configsnapshots/configsnapshot-2010-01-21 08-49-21.xml b/lib/.configsnapshots/configsnapshot-2010-01-21 08-49-21.xml new file mode 100644 index 0000000000000000000000000000000000000000..fcb4b523c4051acadbb36fb5998b26d89636eb83 --- /dev/null +++ b/lib/.configsnapshots/configsnapshot-2010-01-21 08-49-21.xml @@ -0,0 +1,2 @@ +<?xml version="1.0"?> +<pearconfig version="1.0"><php_dir>/Users/bbieber/Documents/workspace/batchval/lib/php</php_dir><ext_dir>/usr/local/lib/php/extensions/no-debug-non-zts-20090626/</ext_dir><cfg_dir>/Users/bbieber/Documents/workspace/batchval/lib/cfg</cfg_dir><doc_dir>/Users/bbieber/Documents/workspace/batchval/lib/docs</doc_dir><bin_dir>/usr/local/bin</bin_dir><data_dir>/Users/bbieber/Documents/workspace/batchval/lib/data</data_dir><www_dir>/Users/bbieber/Documents/workspace/batchval/lib/www</www_dir><test_dir>/Users/bbieber/Documents/workspace/batchval/lib/tests</test_dir><src_dir>/Users/bbieber/Documents/workspace/batchval/lib/src</src_dir><php_bin>/usr/local/bin/php</php_bin><php_ini>/usr/local/lib/php.ini</php_ini><php_prefix></php_prefix><php_suffix></php_suffix></pearconfig> diff --git a/lib/.pear2registry b/lib/.pear2registry new file mode 100644 index 0000000000000000000000000000000000000000..acfed69f6d81490d55229c9f4ed88bf6288868b8 Binary files /dev/null and b/lib/.pear2registry differ diff --git a/lib/.xmlregistry/channels/channel-__uri.xml b/lib/.xmlregistry/channels/channel-__uri.xml new file mode 100644 index 0000000000000000000000000000000000000000..987a5938d21ff15cb9d5a48bd0f9ba85a01366e8 --- /dev/null +++ b/lib/.xmlregistry/channels/channel-__uri.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>__uri</name> + <suggestedalias>__uri</suggestedalias> + <summary>Pseudo-channel for static packages</summary> + <servers> + <primary> + <xmlrpc> + <function version="1.0">****</function> + </xmlrpc> + </primary> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channel-doc.php.net.xml b/lib/.xmlregistry/channels/channel-doc.php.net.xml new file mode 100644 index 0000000000000000000000000000000000000000..d221a21bb166d01bd1460fba227cb8c11378aecb --- /dev/null +++ b/lib/.xmlregistry/channels/channel-doc.php.net.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>doc.php.net</name> + <summary>PHP Documentation team</summary> + <suggestedalias>phpdocs</suggestedalias> + <servers> + <primary> + <rest> + <baseurl type="REST1.0">http://doc.php.net/rest/</baseurl> + <baseurl type="REST1.1">http://doc.php.net/rest/</baseurl> + </rest> + </primary> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channel-pear.php.net.xml b/lib/.xmlregistry/channels/channel-pear.php.net.xml new file mode 100644 index 0000000000000000000000000000000000000000..c7098913388f024799a647718a87e499845383cc --- /dev/null +++ b/lib/.xmlregistry/channels/channel-pear.php.net.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>pear.php.net</name> + <suggestedalias>pear</suggestedalias> + <summary>PHP Extension and Application Repository</summary> + <servers> + <primary> + <rest> + <baseurl type="REST1.0">http://pear.php.net/rest/</baseurl> + <baseurl type="REST1.1">http://pear.php.net/rest/</baseurl> + <baseurl type="REST1.2">http://pear.php.net/rest/</baseurl> + <baseurl type="REST1.3">http://pear.php.net/rest/</baseurl> + </rest> + </primary> + <mirror host="us.pear.php.net"> + <rest> + <baseurl type="REST1.0">http://us.pear.php.net/rest/</baseurl> + <baseurl type="REST1.1">http://us.pear.php.net/rest/</baseurl> + <baseurl type="REST1.2">http://us.pear.php.net/rest/</baseurl> + <baseurl type="REST1.3">http://us.pear.php.net/rest/</baseurl> + </rest> + </mirror> + <mirror host="de.pear.php.net" ssl="yes" port="3452"> + <rest> + <baseurl type="REST1.0">https://de.pear.php.net:3452/rest/</baseurl> + <baseurl type="REST1.1">https://de.pear.php.net:3452/rest/</baseurl> + <baseurl type="REST1.2">https://de.pear.php.net:3452/rest/</baseurl> + <baseurl type="REST1.3">https://de.pear.php.net:3452/rest/</baseurl> + </rest> + </mirror> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channel-pear2.php.net.xml b/lib/.xmlregistry/channels/channel-pear2.php.net.xml new file mode 100644 index 0000000000000000000000000000000000000000..dbcaccb25a38268191e77901c6f3be7714a91477 --- /dev/null +++ b/lib/.xmlregistry/channels/channel-pear2.php.net.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>pear2.php.net</name> + <suggestedalias>pear2</suggestedalias> + <summary>PEAR packages for PHP 5.3+ installed by Pyrus</summary> + <servers> + <primary> + <rest> + <baseurl type="REST1.0">http://pear2.php.net/rest/</baseurl> + <baseurl type="REST1.1">http://pear2.php.net/rest/</baseurl> + <baseurl type="REST1.2">http://pear2.php.net/rest/</baseurl> + <baseurl type="REST1.3">http://pear2.php.net/rest/</baseurl> + </rest> + </primary> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channel-pearhub.org.xml b/lib/.xmlregistry/channels/channel-pearhub.org.xml new file mode 100644 index 0000000000000000000000000000000000000000..5985b4420f545d03a822f99d0b704b23effb1998 --- /dev/null +++ b/lib/.xmlregistry/channels/channel-pearhub.org.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>pearhub.org</name> + <summary>pearhub PEAR channel</summary> + <suggestedalias>pearhub</suggestedalias> + <servers> + <primary> + <rest> + <baseurl type="REST1.0">http://pearhub.org/rest/</baseurl> + <baseurl type="REST1.1">http://pearhub.org/rest/</baseurl> + <baseurl type="REST1.2">http://pearhub.org/rest/</baseurl> + <baseurl type="REST1.3">http://pearhub.org/rest/</baseurl> + </rest> + </primary> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channel-pecl.php.net.xml b/lib/.xmlregistry/channels/channel-pecl.php.net.xml new file mode 100644 index 0000000000000000000000000000000000000000..cd6b257fc15d400fb0f1bcc38cdf57437c288a2b --- /dev/null +++ b/lib/.xmlregistry/channels/channel-pecl.php.net.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>pecl.php.net</name> + <suggestedalias>pecl</suggestedalias> + <summary>PHP Extension Community Library</summary> + <validatepackage version="1.0">PEAR_Validator_PECL</validatepackage> + <servers> + <primary> + <xmlrpc> + <function version="1.0">logintest</function> + <function version="1.0">package.listLatestReleases</function> + <function version="1.0">package.listAll</function> + <function version="1.0">package.info</function> + <function version="1.0">package.getDownloadURL</function> + <function version="1.0">package.getDepDownloadURL</function> + <function version="1.0">package.search</function> + <function version="1.0">channel.listAll</function> + </xmlrpc> + <rest> + <baseurl type="REST1.0">http://pecl.php.net/rest/</baseurl> + <baseurl type="REST1.1">http://pecl.php.net/rest/</baseurl> + </rest> + </primary> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channelalias-__uri.txt b/lib/.xmlregistry/channels/channelalias-__uri.txt new file mode 100644 index 0000000000000000000000000000000000000000..9e550d0400c45929b0ab93c775a8a3f04c66f6b9 --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-__uri.txt @@ -0,0 +1 @@ +__uri \ No newline at end of file diff --git a/lib/.xmlregistry/channels/channelalias-pear.txt b/lib/.xmlregistry/channels/channelalias-pear.txt new file mode 100644 index 0000000000000000000000000000000000000000..f4730b991be41ec5fec3805cd68ce31a53963ca5 --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-pear.txt @@ -0,0 +1 @@ +pear.php.net \ No newline at end of file diff --git a/lib/.xmlregistry/channels/channelalias-pear2.txt b/lib/.xmlregistry/channels/channelalias-pear2.txt new file mode 100644 index 0000000000000000000000000000000000000000..7911749f249888395a8754f5cb8ec2880c4039cb --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-pear2.txt @@ -0,0 +1 @@ +pear2.php.net \ No newline at end of file diff --git a/lib/.xmlregistry/channels/channelalias-pearhub.txt b/lib/.xmlregistry/channels/channelalias-pearhub.txt new file mode 100644 index 0000000000000000000000000000000000000000..6203fad9defa2feb5581a4877298d475e30537e6 --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-pearhub.txt @@ -0,0 +1 @@ +pearhub.org \ No newline at end of file diff --git a/lib/.xmlregistry/channels/channelalias-pecl.txt b/lib/.xmlregistry/channels/channelalias-pecl.txt new file mode 100644 index 0000000000000000000000000000000000000000..2de48f1b005bd518dbb92e2b68df9d49431398d7 --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-pecl.txt @@ -0,0 +1 @@ +pecl.php.net \ No newline at end of file diff --git a/lib/.xmlregistry/channels/channelalias-phpdocs.txt b/lib/.xmlregistry/channels/channelalias-phpdocs.txt new file mode 100644 index 0000000000000000000000000000000000000000..1e733d9cdf2d0df16d75b2d3e558d8d290158e9a --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-phpdocs.txt @@ -0,0 +1 @@ +doc.php.net \ No newline at end of file diff --git a/lib/.xmlregistry/packages/pear.php.net/HTTP_Request2/0.5.1-info.xml b/lib/.xmlregistry/packages/pear.php.net/HTTP_Request2/0.5.1-info.xml new file mode 100644 index 0000000000000000000000000000000000000000..d96e7095d2b7d4a3073a3b816c7ba224b206513e --- /dev/null +++ b/lib/.xmlregistry/packages/pear.php.net/HTTP_Request2/0.5.1-info.xml @@ -0,0 +1,279 @@ +<?xml version="1.0" encoding="UTF-8"?> +<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> + <name>HTTP_Request2</name> + <channel>pear.php.net</channel> + <extends>HTTP_Request</extends> + <summary>Provides an easy way to perform HTTP requests.</summary> + <description>PHP5 rewrite of HTTP_Request package. Provides cleaner API and pluggable +Adapters. Currently available are: + * Socket adapter, based on old HTTP_Request code, + * Curl adapter, wraps around PHP's cURL extension, + * Mock adapter, to use for testing packages dependent on HTTP_Request2. +Supports POST requests with data and file uploads, basic and digest +authentication, cookies, proxies, gzip and deflate encodings, redirects, +monitoring the request progress with Observers...</description> + <lead> + <name>Alexey Borzov</name> + <user>avb</user> + <email>avb@php.net</email> + <active>yes</active> + </lead> + <date>2010-01-21</date> + <time>08:49:26</time> + <version> + <release>0.5.1</release> + <api>0.5.0</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license> + <notes> +* Content-Type request header is no longer removed for POST and PUT requests + with empty request body (request #16799). +* CURLOPT_NOBODY option is now set when doing HEAD requests with Curl adapter. + </notes> + <contents> + <dir name="/"> + <file baseinstalldir="HTTP" md5sum="1fb55dfe18831f8fe6280280e72ad216" name="tests/_files/response_headers" role="test"/> + <file baseinstalldir="HTTP" md5sum="722328bfe89a9c9f7de5a020ed2c4589" name="tests/_files/response_gzip_broken" role="test"/> + <file baseinstalldir="HTTP" md5sum="c36530c79c044fde1745b244c38d381f" name="tests/_files/response_gzip" role="test"/> + <file baseinstalldir="HTTP" md5sum="12d80db889f528922a31b5c03f693647" name="tests/_files/response_deflate" role="test"/> + <file baseinstalldir="HTTP" md5sum="d1d2beb78782f56e8611100a009fb1f6" name="tests/_files/response_cookies" role="test"/> + <file baseinstalldir="HTTP" md5sum="120ea8a25e5d487bf68b5f7096440019" name="tests/_files/plaintext.txt" role="test"/> + <file baseinstalldir="HTTP" md5sum="fc94fb0c3ed8a8f909dbc7630a0987ff" name="tests/_files/empty.gif" role="test"/> + <file baseinstalldir="HTTP" md5sum="22d7f11b85dd00bd8919a4226a5a0388" name="tests/_files/bug_15305" role="test"/> + <file baseinstalldir="HTTP" md5sum="96e929eb014758b7b5712be5c45110b9" name="tests/Request2Test.php" role="test"/> + <file baseinstalldir="HTTP" md5sum="7ae3f0ef61d832d166671b044191efbf" name="tests/Request2/ResponseTest.php" role="test"/> + <file baseinstalldir="HTTP" md5sum="ec1c8a2a8ab448183ed831ebd1c20518" name="tests/Request2/MultipartBodyTest.php" role="test"/> + <file baseinstalldir="HTTP" md5sum="3f9867c9347815f15eac65c87b5c55e3" name="tests/Request2/AllTests.php" role="test"/> + <file baseinstalldir="HTTP" md5sum="fb8e45c13ca3fb1c8594e22df9ab90d2" name="tests/Request2/Adapter/MockTest.php" role="test"/> + <file baseinstalldir="HTTP" md5sum="d389619e0dad1f891a8b18117177496f" name="tests/Request2/Adapter/AllTests.php" role="test"/> + <file baseinstalldir="HTTP" md5sum="f8691b81e639395523bc828b569fa716" name="tests/ObserverTest.php" role="test"/> + <file baseinstalldir="HTTP" md5sum="f24af905aa262adefa4f692e2fe2a167" name="tests/AllTests.php" role="test"/> + <file baseinstalldir="HTTP" md5sum="07bfa5cff153994f244ceee9bd77de2a" name="Request2/Response.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file baseinstalldir="HTTP" md5sum="10b4d5d88f298a4e2fe7df08e5a218bb" name="Request2/Observer/Log.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file baseinstalldir="HTTP" md5sum="20b26f829852793a0e545c5df648a9b6" name="Request2/MultipartBody.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file baseinstalldir="HTTP" md5sum="a0b53a6710a47bc718dabc0edef36555" name="Request2/Exception.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file baseinstalldir="HTTP" md5sum="496dda12e9ca16561d9237651b1065e3" name="Request2/Adapter/Socket.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file baseinstalldir="HTTP" md5sum="ee8a541eb43be5cd04fd392770f90548" name="Request2/Adapter/Mock.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file baseinstalldir="HTTP" md5sum="90c77d4b4425b4bb63cf6ee921e5e3d1" name="Request2/Adapter/Curl.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file baseinstalldir="HTTP" md5sum="043e3d6edeeec91f7d21bf825090c2dd" name="Request2/Adapter.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file baseinstalldir="HTTP" md5sum="588ec605871ca474a8bcc3bf8f8035fc" name="Request2.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file baseinstalldir="HTTP" md5sum="7e6017dfdf042dbd443ce6c8c024f40d" name="docs/examples/upload-rapidshare.php" role="doc"/> + </dir> + </contents> + <dependencies> + <required> + <php> + <min>5.1.4</min> + </php> + <pearinstaller> + <min>1.5.4</min> + </pearinstaller> + <package> + <name>Net_URL2</name> + <channel>pear.php.net</channel> + <min>0.2.0</min> + </package> + </required> + <optional> + <extension> + <name>curl</name> + </extension> + <extension> + <name>fileinfo</name> + </extension> + <extension> + <name>zlib</name> + </extension> + <extension> + <name>openssl</name> + </extension> + </optional> + </dependencies> + <phprelease> + <changelog> + <release> + <version> + <release>0.5.0</release> + <api>0.5.0</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <date>2009-11-18</date> + <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license> + <notes> +* Redirect support added, new configuration parameters 'follow_redirects', + 'max_redirects' and 'strict_redirects' available + +* Implemented workaround for PHP bug #47204, Curl Adapter can now handle + Digest authentication and redirects when doing POST requests, unfortunately + this requires loading the entire request body into memory. +* Config parameter 'use_brackets' is propagated to created instances of Net_URL2 +* Prevent memory leaks due to circular references (request #16646) +* Fixed a misleading error message when timing out due to default_socket_timeout +* HTTP_Request2::setBody() can now accept an instance of HTTP_Request2_MultipartBody + without trying to convert it to string +* Calling HTTP_Request2::setBody() now clears post parameters and uploads + </notes> + </release> + <release> + <version> + <release>0.4.1</release> + <api>0.4.0</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <date>2009-09-14</date> + <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license> + <notes> +* Decoding of gzipped responses failed if mbstring.func_overload was enabled + (bug #16555) +* Changed boundary generation in multipart bodies to work correctly with + rapidshare.com, added first usage example: file uploading to rapidshare.com +* Added forgotten optional dependency on OpenSSL PHP extension + </notes> + </release> + <release> + <version> + <release>0.4.0</release> + <api>0.4.0</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <date>2009-05-03</date> + <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license> + <notes> +* Added 'store_body' config parameter, if set to false it will prevent storing + the response body in Response object (request #15881) +* HTTP_Request2::setHeader() method now works as documented, setHeader('name') + will remove the 'name' header, while setHeader('name', '') will set 'name' + header to empty value (bug #15937) +* Custom 'Host' header will not be overwritten by generated one (bug #16146) +* When trying to reuse the connected socket in Socket adapter, make sure that + it is still connected (bug #16149) + </notes> + </release> + <release> + <version> + <release>0.3.0</release> + <api>0.3.0</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <date>2009-01-28</date> + <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license> + <notes> +API changes: + * Removed HTTP_Request2::getConfigValue() method + +Feature additions: + * Added digest authentication (RFC 2617) support to Socket adapter. Thanks + to Tom Snyder (tomsn at inetoffice dot com) who sent me a prototype + implementation for HTTP_Request a couple of years ago. + * Added HTTPS proxy support to Socket adapter, this works through CONNECT + request described in RFC 2817. + * Mock adapter can now throw an Exception instead of returning a response + if Exception object is added via its addResponse() method (request #15629) + +Other changes and fixes: + * Support RFC 3986 by not encoding '~' in POST body (request #15368) + * Prevent an error with particular versions of PHP and Curl (bug #15617) + * Regular expressions used in HTTP_Request2 are now class constants + (request #15630) + * Curl adapter now throws an exception in case of malformed (non-HTTP) + response rather than dies with a fatal error (bug #15716) + * Curl handle wasn't closed in Curl adapter in case of error (bug #15721) + * Curl adapter sent an extra 'sentHeaders' event and returned bogus + response status when server returned 100-Continue response (bug #15785) + </notes> + </release> + <release> + <version> + <release>0.2.0</release> + <api>0.2.0</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <date>2009-01-07</date> + <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license> + <notes> +API changes: + * HTTP_Request2::getConfigValue() is deprecated and will be removed in next + release. Use HTTP_Request2::getConfig(). + * Changed HTTP_Request2::setConfig() to accept a pair of parameter name and + parameter value in addition to array('parameter name' => 'value') + * Added HTTP_Request2::getConfig() method that can return a single + configuration parameter or the whole configuration array + +Other additions and changes: + * Added a debug Observer that can log request progress to a file or an + instance of PEAR::Log (thanks to David Jean Louis, request #15424) + * Added a new 'timeout' parameter that limits total number of seconds + a request can take (see requests #5735 and #8964) + * Added various SSL protocol options: 'ssl_verify_peer', 'ssl_verify_host', + 'ssl_cafile', 'ssl_capath', 'ssl_local_cert', 'ssl_passphrase'. Note that + 'ssl_verify_host' option behaves differently in Socket and Curl Adapters: + http://bugs.php.net/bug.php?id=47030 + +Fixes: + * Fixed 'data error' when processing response encoded by 'deflate' + encoding (bug #15305) + * Curl Adapter now passes full request headers in 'sentHeaders' event + </notes> + </release> + <release> + <version> + <release>0.1.0</release> + <api>0.1.0</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <date>2008-11-17</date> + <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license> + <notes> +Initial release. The features supported are mostly the same as those of +HTTP_Request, with the following additional feature requests implemented: + * cURL extension support (request #5463) + * It is now possible to monitor the file upload progress with Observers + (request #7630) + * Added 'sentHeaders' notification providing the request headers to the + Observers (request #7633) + * Added support for 'deflate' encoding (request #11246) + </notes> + </release> + </changelog> + </phprelease> +</package> diff --git a/lib/.xmlregistry/packages/pear.php.net/Net_URL2/0.3.0-info.xml b/lib/.xmlregistry/packages/pear.php.net/Net_URL2/0.3.0-info.xml new file mode 100644 index 0000000000000000000000000000000000000000..94e2687faf6a8f4fbf5ee81ad538e9d47770ac91 --- /dev/null +++ b/lib/.xmlregistry/packages/pear.php.net/Net_URL2/0.3.0-info.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.5.1" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> + <name>Net_URL2</name> + <channel>pear.php.net</channel> + <summary>Class for parsing and handling URL.</summary> + <description>Provides parsing of URLs into their constituent parts (scheme, host, path etc.), URL generation, and resolving of relative URLs.</description> + <lead> + <name>David Coallier</name> + <user>davidc</user> + <email>davidc@php.net</email> + <active>yes</active> + </lead> + <lead> + <name>Christian Schmidt</name> + <user>schmidt</user> + <email>schmidt@php.net</email> + <active>yes</active> + </lead> + <date>2010-01-21</date> + <time>08:49:21</time> + <version> + <release>0.3.0</release> + <api>0.3.0</api> + </version> + <stability> + <release>beta</release> + <api>beta</api> + </stability> + <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license> + <notes>* Fixed #14399 (Errors in URL parsing (items #1 and #3)) +* Fixed #14735 (Encode query string values) +* Fixed #15546 (Add adding __toString()) +* Fixed #15367 (Use RFC 3986-compliant version of rawurlencode() in PHP < 5.2) +* Fixed #14289 (Add __get() and __set())</notes> + <contents> + <dir name="/"> + <file baseinstalldir="Net" name="URL2.php" role="php" md5sum="8a713828923fe747e969966d1d297087"/> + <file baseinstalldir="Net" name="docs/example.php" role="doc" md5sum="587a224d39fbffa47cacb9fb67b51da1"/> + <file baseinstalldir="Net" name="docs/6470.php" role="doc" md5sum="ea8b73061588566519fd0e55a230f2a6"/> + </dir> + </contents> + <dependencies> + <required> + <php> + <min>5.0</min> + </php> + <pearinstaller> + <min>1.4.0b1</min> + </pearinstaller> + </required> + </dependencies> + <phprelease> + <changelog> + <release> + <version> + <release>0.2.0</release> + <api>0.2.0</api> + </version> + <stability> + <release>beta</release> + <api>beta</api> + </stability> + <date>2008-06-18</date> + <license>BSD</license> + <notes>Major rewrite to comply with RFC3986 (bug 11574). + Much better support for resolving relative URLs. + WARNING: Method and property names has changed to reflect the terminology used in the RFC - THIS RELEASE IS NOT BACKWARDS COMPATIBLE WITH VERSION 0.1.0.</notes> + </release> + <release> + <version> + <release>0.1.0</release> + <api>0.1.0</api> + </version> + <stability> + <release>beta</release> + <api>beta</api> + </stability> + <date>2007-05-08</date> + <license>BSD</license> + <notes>Convert to PHP5 only. PHP4 users should continue with version 1.0.15</notes> + </release> + </changelog> + </phprelease> +</package> diff --git a/lib/.xmlregistry/packages/pear.php.net/Services_W3C_HTMLValidator/1.0.0RC2-info.xml b/lib/.xmlregistry/packages/pear.php.net/Services_W3C_HTMLValidator/1.0.0RC2-info.xml new file mode 100644 index 0000000000000000000000000000000000000000..e569295045a030a44f755cc7740ff480f08a43f8 --- /dev/null +++ b/lib/.xmlregistry/packages/pear.php.net/Services_W3C_HTMLValidator/1.0.0RC2-info.xml @@ -0,0 +1,193 @@ +<?xml version="1.0" encoding="UTF-8"?> +<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.8.0alpha1" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> + <name>Services_W3C_HTMLValidator</name> + <channel>pear.php.net</channel> + <summary>An Object Oriented Interface to the W3C HTML Validator service.</summary> + <description>This package provides an object oriented interface to the API +of the W3 HTML Validator application (http://validator.w3.org/). +With this package you can connect to a running instance of the validator and +retrieve the validation results (true|false) as well as the errors and warnings +for a web page. + +By using the SOAP 1.2 output format from the validator, you are returned simple +objects containing all the information from the validator. With this package it is +trivial to build a validation system for web publishing.</description> + <lead> + <name>Brett Bieber</name> + <user>saltybeagle</user> + <email>brett.bieber@gmail.com</email> + <active>yes</active> + </lead> + <date>2010-01-21</date> + <time>08:49:33</time> + <version> + <release>1.0.0RC2</release> + <api>1.0.0RC2</api> + </version> + <stability> + <release>beta</release> + <api>beta</api> + </stability> + <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license> + <notes> +* Switch to HTTP_Request2 + </notes> + <contents> + <dir name="/"> + <file baseinstalldir="Services/W3C" md5sum="45c6625f05fe3a6473a5699277d30ee5" name="tests/Services/W3C/HTMLValidatorTest.php" role="test"/> + <file baseinstalldir="Services/W3C" md5sum="a6c033d1b4358eb80358db58939f6f58" name="tests/AllTests.php" role="test"/> + <file baseinstalldir="Services/W3C" md5sum="a6cc77ff4a49c95d05d358648e976e82" name="HTMLValidator/Warning.php" role="php"/> + <file baseinstalldir="Services/W3C" md5sum="9190cab9f04fb512415377c3f4f15d1c" name="HTMLValidator/Response.php" role="php"/> + <file baseinstalldir="Services/W3C" md5sum="f48d22f39da255d34c467c58b5e86a53" name="HTMLValidator/Message.php" role="php"/> + <file baseinstalldir="Services/W3C" md5sum="9edcf30a27e376a3260f674cf829ccd5" name="HTMLValidator/Error.php" role="php"/> + <file baseinstalldir="Services/W3C" md5sum="fece8e09b2add1806b97d6b9711c3eb3" name="HTMLValidator.php" role="php"/> + <file baseinstalldir="Services/W3C" md5sum="dc6608c7334e527a4d641cfa580869dc" name="docs/examples/validate_responsexml.php" role="doc"/> + <file baseinstalldir="Services/W3C" md5sum="aefc4bfdaf6fdb3a40a6a1cbf41a2972" name="docs/examples/validate_fragment.php" role="doc"/> + <file baseinstalldir="Services/W3C" md5sum="44a9e0b1d85afc5b965034419dd627fc" name="docs/examples/validate_byuri.php" role="doc"/> + <file baseinstalldir="Services/W3C" md5sum="85376a75fda57cbb7236fb777192a832" name="docs/examples/validate_byfile.php" role="doc"/> + <file baseinstalldir="Services/W3C" md5sum="d55c429e13bb7d0c0b66a0c675e3c3e8" name="docs/examples/PHPUnit/ValidationSuite.php" role="doc"/> + <file baseinstalldir="Services/W3C" md5sum="63c4bda34c6babc7feb9fcb64e1e63ff" name="docs/examples/PHPUnit/URIValidationTest.php" role="doc"/> + <file baseinstalldir="Services/W3C" md5sum="f31575f3932b5c0cbb9ceb6c8eca7a7b" name="docs/examples/example.html" role="doc"/> + </dir> + </contents> + <dependencies> + <required> + <php> + <min>5.0.0</min> + </php> + <pearinstaller> + <min>1.4.3</min> + </pearinstaller> + <package> + <name>HTTP_Request2</name> + <channel>pear.php.net</channel> + <min>0.2.0</min> + </package> + </required> + </dependencies> + <phprelease> + <changelog> + <release> + <version> + <release>0.0.1</release> + <api>0.0.1</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <date>2006-11-28</date> + <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license> + <notes> +First release offers basic functionality only validate($uri) method. + </notes> + </release> + <release> + <version> + <release>0.1.0</release> + <api>0.1.0</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <date>2006-12-03</date> + <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license> + <notes> +Added method to validate uploaded file validateFile(); +Add method to validate html fragment validateFragment(); +Added a method to the Response object isValid(). +Add example for validateFragment and validateFile. +Fix phpdoc commenting and various CS fixes (thanks David Coallier). + </notes> + </release> + <release> + <version> + <release>0.1.5</release> + <api>0.1.5</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <date>2006-12-05</date> + <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license> + <notes> +Switched methods from private to protected to allow overriding. +Move all variables which were int 0|1 to booleans (thanks Christian Weiske). + </notes> + </release> + <release> + <version> + <release>0.2.0</release> + <api>0.2.0</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <date>2007-06-07</date> + <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license> + <notes> +Switch last remaining private member variable to protected (thanks Christian Weiske). +Move examples into docs directory (thanks Mark Wiesemann) +Change channel from pear.saltybeagle.com to pear.php.net + </notes> + </release> + <release> + <version> + <release>0.3.0</release> + <api>0.3.0</api> + </version> + <stability> + <release>beta</release> + <api>beta</api> + </stability> + <date>2007-06-14</date> + <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license> + <notes> +New features - return error and warning source context, unique message id, and explanations for the error and warnings. +http://www.w3.org/Bugs/Public/show_bug.cgi?id=4489 + +These features will be released in the next version of the W3C Validator (version 0.8.0), until the next release users can test the output by assigning the $validator->validator_uri to the CVS version for the validator http://qa-dev.w3.org/wmvs/HEAD/check. + </notes> + </release> + <release> + <version> + <release>1.0.0RC1</release> + <api>1.0.0RC1</api> + </version> + <stability> + <release>beta</release> + <api>beta</api> + </stability> + <date>2007-07-25</date> + <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license> + <notes> +New features - return error and warning source context, unique +message id, and explanations for the error and warnings. +http://www.w3.org/Bugs/Public/show_bug.cgi?id=4489 + +These features are included in the latest version of the W3C Validator (version +0.8.0). +Unit Tests now included, additional documentation and coding standards fixes. + </notes> + </release> + <release> + <version> + <release>1.0.0RC2</release> + <api>1.0.0RC2</api> + </version> + <stability> + <release>beta</release> + <api>beta</api> + </stability> + <date>2009-01-20</date> + <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license> + <notes> +* Switch to HTTP_Request2 + </notes> + </release> + </changelog> + </phprelease> +</package> diff --git a/lib/.xmlregistry/packages/pearhub.org/Spider/0.1.0-info.xml b/lib/.xmlregistry/packages/pearhub.org/Spider/0.1.0-info.xml new file mode 100644 index 0000000000000000000000000000000000000000..3f2d07e20f1f90538602155f5a45f48943b9da5c --- /dev/null +++ b/lib/.xmlregistry/packages/pearhub.org/Spider/0.1.0-info.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<package xmlns="http://pear.php.net/dtd/package-2.1" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.1" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.1 http://pear.php.net/dtd/package-2.1.xsd"> + <name>Spider</name> + <channel>pearhub.org</channel> + <summary>Package Spider summary. +</summary> + <description> +Package detailed description here (found in README) +</description> + <lead> + <name>Brett Bieber</name> + <user>saltybeagle</user> + <email>saltybeagle@php.net</email> + <active>yes</active> + </lead> + <date>2010-01-21</date> + <time>08:57:07</time> + <version> + <release>0.1.0</release> + <api>0.1.0</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license> + <notes>Package PEAR2_Spider release notes for version 0.1.0. + +Package PEAR2_Spider API release notes for version 0.1.0.</notes> + <contents> + <dir name="/"> + <file role="php" name="src/Spider/UriIterator.php" md5sum="8f47d38de045dfe754a9c197b0845af2"/> + <file role="php" name="src/Spider/UriFilterInterface.php" md5sum="dcace02f51e3c1f12005b3765a97ccc0"/> + <file role="php" name="src/Spider/TidyParser.php" md5sum="989764252f60b4d0c0a4a4cc4c296254"/> + <file role="php" name="src/Spider/StyleSheetLogger.php" md5sum="4e323ae9f3129deb4aa519129055d9af"/> + <file role="php" name="src/Spider/ParserInterface.php" md5sum="140add631a24ee19cc974e5d190fb526"/> + <file role="php" name="src/Spider/Parser.php" md5sum="ce6879e89e1f082ff44821ceb4c429bf"/> + <file role="php" name="src/Spider/PageLogger.php" md5sum="f05cc3d2e54fa03e143546ec0a84eb73"/> + <file role="php" name="src/Spider/MailtoFilter.php" md5sum="3c116ba7fc28fc47fa4a523212f0363a"/> + <file role="php" name="src/Spider/LoggerAbstract.php" md5sum="c26904afb26b2ee2866c18f130aa0afb"/> + <file role="php" name="src/Spider/Logger.php" md5sum="e40d93bb8399c2f7dd1d9ccc7d654516"/> + <file role="php" name="src/Spider/JavaScriptLogger.php" md5sum="2a17632c40359e9215d64426d89abd6c"/> + <file role="php" name="src/Spider/Downloader.php" md5sum="8388b6feeb60b8c433c7151547106804"/> + <file role="php" name="src/Spider/DbLogger.php" md5sum="985aa51e4c93e76b6848c1e7060c1d1a"/> + <file role="php" name="src/Spider/AnchorFilter.php" md5sum="033b1ae3ea8fee40076f728b4a95fd2b"/> + <file role="php" name="src/Spider.php" md5sum="219c9dd9003e1f06a3843da1777f6aa9"/> + <file role="doc" name="examples/results.db" md5sum="8967b7f9bf5fedf351c02cd76e6e0e8b"/> + <file role="doc" name="examples/batch_validator.php" md5sum="87bdb9b37fc5ff22e3d32ee2f2147093"/> + <file role="doc" name="examples/basic.php" md5sum="ae8b39ff256de45d662b9114cbf2d9df"/> + </dir> + </contents> + <dependencies> + <required> + <php> + <min>5.2.0</min> + </php> + <pearinstaller> + <min>2.0.0a1</min> + </pearinstaller> + </required> + </dependencies> + <phprelease/> +</package> diff --git a/lib/docs/HTTP_Request2/HTTP/docs/examples/upload-rapidshare.php b/lib/docs/HTTP_Request2/HTTP/docs/examples/upload-rapidshare.php new file mode 100644 index 0000000000000000000000000000000000000000..9773a94c48fed08bb23ad5beffda73d05ff5cd32 --- /dev/null +++ b/lib/docs/HTTP_Request2/HTTP/docs/examples/upload-rapidshare.php @@ -0,0 +1,60 @@ +<?php +/** + * Usage example for HTTP_Request2 package: uploading a file to rapidshare.com + * + * Inspired by Perl usage example: http://images.rapidshare.com/software/rsapi.pl + * Rapidshare API description: http://rapidshare.com/dev.html + * + * $Id: upload-rapidshare.php 287307 2009-08-14 15:40:22Z avb $ + */ + +require_once 'HTTP/Request2.php'; + +// You'll probably want to change this +$filename = '/etc/passwd'; + +try { + // First step: get an available upload server + $request = new HTTP_Request2( + 'http://rapidshare.com/cgi-bin/rsapi.cgi?sub=nextuploadserver_v1' + ); + $server = $request->send()->getBody(); + if (!preg_match('/^(\\d+)$/', $server)) { + throw new Exception("Invalid upload server: {$server}"); + } + + // Calculate file hash, we'll use it later to check upload + if (false === ($hash = @md5_file($filename))) { + throw new Exception("Cannot calculate MD5 hash of '{$filename}'"); + } + + // Second step: upload a file to the available server + $uploader = new HTTP_Request2( + "http://rs{$server}l3.rapidshare.com/cgi-bin/upload.cgi", + HTTP_Request2::METHOD_POST + ); + // Adding the file + $uploader->addUpload('filecontent', $filename); + // This will tell server to return program-friendly output + $uploader->addPostParameter('rsapi_v1', '1'); + + $response = $uploader->send()->getBody(); + if (!preg_match_all('/^(File[^=]+)=(.+)$/m', $response, $m, PREG_SET_ORDER)) { + throw new Exception("Invalid response: {$response}"); + } + $rspAry = array(); + foreach ($m as $item) { + $rspAry[$item[1]] = $item[2]; + } + // Check that uploaded file has the same hash + if (empty($rspAry['File1.4'])) { + throw new Exception("MD5 hash data not found in response"); + } elseif ($hash != strtolower($rspAry['File1.4'])) { + throw new Exception("Upload failed, local MD5 is {$hash}, uploaded MD5 is {$rspAry['File1.4']}"); + } + echo "Upload succeeded\nDownload link: {$rspAry['File1.1']}\nDelete link: {$rspAry['File1.2']}\n"; + +} catch (Exception $e) { + echo "Error: " . $e->getMessage(); +} +?> diff --git a/lib/docs/Net_URL2/Net/docs/6470.php b/lib/docs/Net_URL2/Net/docs/6470.php new file mode 100644 index 0000000000000000000000000000000000000000..104de8960852ebff997e4d9d17a209c35c19cb5d --- /dev/null +++ b/lib/docs/Net_URL2/Net/docs/6470.php @@ -0,0 +1,75 @@ +<?php +// +-----------------------------------------------------------------------+ +// | Copyright (c) 2002-2003, Richard Heyes | +// | All rights reserved. | +// | | +// | Redistribution and use in source and binary forms, with or without | +// | modification, are permitted provided that the following conditions | +// | are met: | +// | | +// | o Redistributions of source code must retain the above copyright | +// | notice, this list of conditions and the following disclaimer. | +// | o Redistributions in binary form must reproduce the above copyright | +// | notice, this list of conditions and the following disclaimer in the | +// | documentation and/or other materials provided with the distribution.| +// | o The names of the authors may not be used to endorse or promote | +// | products derived from this software without specific prior written | +// | permission. | +// | | +// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | +// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | +// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | +// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | +// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | +// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | +// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | +// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | +// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | +// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | +// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | +// | | +// +-----------------------------------------------------------------------+ +// | Author: Richard Heyes <richard at php net> | +// +-----------------------------------------------------------------------+ +// $Id: 6470.php 235190 2007-05-08 00:04:54Z davidc $ +/** +* This example will decode the url given and display its +* constituent parts. +*/ + error_reporting(E_ALL | E_STRICT); + + require_once 'Net/URL2.php'; + + //$url = &new Net_URL2('https://www.example.com/foo/bar/index.php?foo=bar'); + Net_URL2::setOption('encode_query_keys', true); + $url = new Net_URL2; + +?> +<html> +<body> + +<pre> +Protocol...: <?php echo $url->protocol; ?> + +Username...: <?php echo $url->user; ?> + +Password...: <?php echo $url->pass; ?> + +Server.....: <?php echo $url->host; ?> + +Port.......: <?php $url->port; ?> + +File/path..: <?php $url->path; ?> + +Querystring: <?php print_r($url->querystring); ?> + +Anchor.....: <?php echo $url->anchor;?> + +Full URL...: <?php echo $url->getUrl(); ?> + + +Resolve path (/.././/foo/bar/joe/./././../jabba): <b><?php Net_URL2::resolvePath('/.././/foo/bar/joe/./././../jabba'); ?></b> +</pre> + +</body> +</html> diff --git a/lib/docs/Net_URL2/Net/docs/example.php b/lib/docs/Net_URL2/Net/docs/example.php new file mode 100644 index 0000000000000000000000000000000000000000..d2725ba5aaa8d854de39d43e1e44fbbd6783c1a9 --- /dev/null +++ b/lib/docs/Net_URL2/Net/docs/example.php @@ -0,0 +1,74 @@ +<?php +// +-----------------------------------------------------------------------+ +// | Copyright (c) 2002-2003, Richard Heyes | +// | All rights reserved. | +// | | +// | Redistribution and use in source and binary forms, with or without | +// | modification, are permitted provided that the following conditions | +// | are met: | +// | | +// | o Redistributions of source code must retain the above copyright | +// | notice, this list of conditions and the following disclaimer. | +// | o Redistributions in binary form must reproduce the above copyright | +// | notice, this list of conditions and the following disclaimer in the | +// | documentation and/or other materials provided with the distribution.| +// | o The names of the authors may not be used to endorse or promote | +// | products derived from this software without specific prior written | +// | permission. | +// | | +// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | +// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | +// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | +// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | +// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | +// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | +// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | +// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | +// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | +// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | +// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | +// | | +// +-----------------------------------------------------------------------+ +// | Author: Richard Heyes <richard at php net> | +// +-----------------------------------------------------------------------+ +// $Id: example.php 235190 2007-05-08 00:04:54Z davidc $ +/** +* This example will decode the url given and display its +* constituent parts. +*/ + error_reporting(E_ALL | E_STRICT); + + //include('../URL2.php'); + include('Net/URL2.php'); + + //$url = &new Net_URL2('https://www.example.com/foo/bar/index.php?foo=bar'); + $url = new Net_URL2('https://example.com/pls/portal30/PORTAL30.wwpob_page.changetabs?p_back_url=http%3A%2F%2Fexample.com%2Fservlet%2Fpage%3F_pageid%3D360%2C366%2C368%2C382%26_dad%3Dportal30%26_schema%3DPORTAL30&foo=bar'); +?> +<html> +<body> + +<pre> +Protocol...: <?php echo $url->protocol; ?> + +Username...: <?php echo $url->user; ?> + +Password...: <?php echo $url->pass; ?> + +Server.....: <?php echo $url->host; ?> + +Port.......: <?php $url->port; ?> + +File/path..: <?php $url->path; ?> + +Querystring: <?php print_r($url->querystring); ?> + +Anchor.....: <?php echo $url->anchor;?> + +Full URL...: <?php echo $url->getUrl(); ?> + + +Resolve path (/.././/foo/bar/joe/./././../jabba): <b><?php Net_URL2::resolvePath('/.././/foo/bar/joe/./././../jabba'); ?></b> +</pre> + +</body> +</html> diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/PHPUnit/URIValidationTest.php b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/PHPUnit/URIValidationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..25f65cbe294d59094738ba06f9ffec2b5b1e83cc --- /dev/null +++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/PHPUnit/URIValidationTest.php @@ -0,0 +1,79 @@ +<?php +/** + * This file tests the validation of the web accessible URIs. + */ + +require_once 'Services/W3C/HTMLValidator.php'; +require_once 'PHPUnit/Framework.php'; + +// Call Services_W3C_HTMLValidatorTest::main() if executed directly. +if (!defined("PHPUnit_MAIN_METHOD")) { + define("PHPUnit_MAIN_METHOD", "URIValidationTest::main"); +} + +class URIValidationTest extends PHPUnit_Framework_TestCase +{ + public $uri = 'http://www.unl.edu/'; + + protected $validator; + + /** + * Runs the test methods of this class. + * + * @access public + * @static + * @return void + */ + public static function main() + { + include_once "PHPUnit/TextUI/TestRunner.php"; + + $suite = new PHPUnit_Framework_TestSuite("URIValidationTest"); + $result = PHPUnit_TextUI_TestRunner::run($suite); + } + + + public function setUp() + { + $this->validator = new Services_W3C_HTMLValidator(); + } + + public function testURI() + { + $r = $this->validator->validate($this->uri); + $this->assertEquals(get_class($r), 'Services_W3C_HTMLValidator_Response'); + if (!$r->isValid()) { + $message = $this->constructErrorMessage($r); + } else { + $message = $r->uri.' is Valid'; + } + $this->assertTrue($r->isValid(), $message); + } + + public function testFile() + { + $r = $this->validator->validate($this->uri); + $this->assertEquals(get_class($r), 'Services_W3C_HTMLValidator_Response'); + if (!$r->isValid()) { + $message = $this->constructErrorMessage($r); + } else { + $message = $r->uri.' is Valid'; + } + $this->assertTrue($r->isValid(), $message); + } + + protected function constructErrorMessage(&$r) + { + $message = $r->uri.' is NOT VALID: '.count($r->errors).' Errors -'.PHP_EOL; + foreach ($r->errors as $error) { + $message .= ' - '.$error->message.PHP_EOL; + } + return $message; + } +} + +// Call Services_W3C_HTMLValidatorTest::main() if file is executed directly. +if (PHPUnit_MAIN_METHOD == "URIValidationTest::main") { + URIValidationTest::main(); +} +?> \ No newline at end of file diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/PHPUnit/ValidationSuite.php b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/PHPUnit/ValidationSuite.php new file mode 100644 index 0000000000000000000000000000000000000000..cfeeaee834a5aef0dd7304e47750ae75ae3f8505 --- /dev/null +++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/PHPUnit/ValidationSuite.php @@ -0,0 +1,57 @@ +<?php + +if (!defined("PHPUnit_MAIN_METHOD")) { + define("PHPUnit_MAIN_METHOD", "ValidationTestSuite::main"); +} + + +require_once 'PHPUnit/Framework.php'; +require_once "PHPUnit/Framework/TestCase.php"; +require_once "PHPUnit/Framework/TestSuite.php"; +require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'URIValidationTest.php'; + +class ValidationTestSuite extends PHPUnit_Framework_TestSuite +{ + function __construct($name) + { + + } + + /** + * Runs the test suite. + * + * @return unknown + */ + public static function main() + { + include_once "PHPUnit/TextUI/TestRunner.php"; + PHPUnit_TextUI_TestRunner::run(self::suite()); + } + + /** + * Adds the Services_W3C_HTMLValidatorTest suite. + * + * @return $suite + */ + public static function suite() + { + $uris_to_check = array('http://www.unl.edu/', + 'http://events.unl.edu/', + 'http://www.google.com/'); + $suite = new PHPUnit_Framework_TestSuite('ValidationTestSuite tests'); + /** Add tests, if there is. */ + foreach ($uris_to_check as $uri) { + $test = new URIValidationTest('testURI'); + $test->uri = $uri; + $suite->addTest($test); + } + + return $suite; + } +} + +// Call Services_W3C_HTMLValidatorTest::main() if file is executed directly. +if (PHPUnit_MAIN_METHOD == "ValidationTestSuite::main") { + ValidationTestSuite::main(); +} +?> \ No newline at end of file diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/example.html b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/example.html new file mode 100644 index 0000000000000000000000000000000000000000..152f21f449e60820ace637016e104827a924ee12 --- /dev/null +++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/example.html @@ -0,0 +1,344 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head><title>Documentation of the Programmatic Interface (API) to The W3C Markup Validation Service</title> + + + + + <link rev="made" href="mailto:www-validator@w3.org"> + <link rev="start" href="http://validator.w3.org/" title="Home Page"> + <style type="text/css" media="all">@import "../base.css";</style> + <meta name="keywords" content="HTML, HyperText Markup Language, Validation, + W3C Markup Validation Service"> + <meta name="description" content="W3C's easy-to-use + HTML validation service, based on an SGML parser."> + <meta name="revision" content="$Id$"><!-- SSI Template Version: $Id$ --></head><body> + <div id="banner"> + <h1 id="title"> + <a href="http://www.w3.org/"><img alt="W3C" id="logo" src="api_files/w3c_home_nb" height="48"></a> + <a href="http://www.w3.org/QA/"><img src="api_files/qa-small.png" alt="QA"></a> + Markup Validation Service</h1> + <span id="versioninfo"><abbr title="version">v</abbr>0.7.4</span> + </div> + <ul class="navbar" id="menu"> + <li><span class="hideme"><a href="#skip" accesskey="2" title="Skip past navigation to main part of page">Skip Navigation</a> |</span> + <a href="http://validator.w3.org/" accesskey="1" title="Go to the Home Page for The W3C Markup Validation Service"><strong>Home</strong></a></li> + <li><a href="http://validator.w3.org/about.html" title="Information About this Service">About...</a></li> + <li><a href="http://validator.w3.org/whatsnew.html" title="The changes made to this service recently">News</a></li> + <li><a href="http://validator.w3.org/docs/" accesskey="3" title="Documentation for this Service">Docs</a></li> + <li><a href="http://validator.w3.org/docs/help.html" title="Help and answers to frequently asked questions">Help & <acronym title="Frequently Asked Questions">FAQ</acronym></a></li> + <li><a href="http://validator.w3.org/feedback.html" accesskey="4" title="How to provide feedback on this service">Feedback</a></li> + </ul> + + <div id="main"><!-- This DIV encapsulates everything in this page - necessary for the positioning --> + + +<div class="doc"> +<h2>Markup Validator Web Service API<br> +SOAP 1.2 validation interface documentation</h2> + +<p>Interface applications with the Markup Validator through its <strong>experimental</strong> API. </p> +<p><strong>Note</strong>: Please be considerate in using this shared, free resource. + Consider <a href="http://validator.w3.org/docs/install.html">Installing your own instance of the validator</a> + for smooth and fast operation. Excessive use of the W3C Validation Service + will be blocked.</p> + + +<h3 id="TableOfContents">Table of Contents</h3> + + <div id="toc"> + <ul> + <li><a href="#requestformat">Validation Request Format</a></li> + <li><a href="#soap12format">SOAP format description</a> + <ul> + <li><a href="#soap12_sample">sample SOAP 1.2 validation response</a></li> + <li><a href="#soap12response">SOAP1.2 response format reference</a></li> + <li><a href="#soap12message">SOAP1.2 atomic message (error or warning) format reference</a></li> + </ul> + </li> + <li><a href="#libs">Libraries</a></li> + <li><a href="#http_headers">Using HTTP headers to know validation results</a></li> + </ul> + </div> + + <p id="skip"></p> + +<h3 id="requestformat">Validation Request Format</h3> + +<p>Below is a table of the parameter you can use to send a query to the W3C Markup Validator.</p> +<p>If you want to use W3c's public validation server, use the parameters below + in conjunction with the following base URI:<br> +<kbd>http://validator.w3.org/check</kbd> <br> +(replace with the address of your own server if you want to call a private instance of the validator)</p> + +<p><strong>Note</strong>: If you wish to call the validator programmatically for a batch of documents, +please make sure that your script will <code>sleep</code> for <strong>at least 1 second</strong> +between requests. The Markup Validation service is a free, public service for all, your respect +is appreciated. thanks.</p> + +<table class="refdoc"> +<tbody><tr> +<th>Parameter</th><th>Description</th><th>Default value</th> +</tr> +<tr> + <th>uri</th> + <td>The <acronym title="Universal Resource Locator">URL</acronym> of the document to validate</td> + <td>None, but either this parameter, or <code>uploaded_file</code>, + or <code>fragment</code> must be given.</td> +</tr> +<tr> + <th>uploaded_file</th> + <td>The document to validate, POSTed as multipart/form-data</td> + <td>None, but either this parameter, or <code>uri</code>, + or <code>fragment</code> must be given.</td> +</tr> +<tr> + <th>fragment</th> + <td>The source of the document to validate. Full documents only. + At the moment, will only work if data is sent with the UTF-8 encoding.</td> + <td>None, but either this parameter, or <code>uri</code>, + or <code>uploaded_file</code> must be given.</td> +</tr> +<tr> + <th>output</th> + <td>triggers the various outputs formats of the validator. If unset, the usual + Web format will be sent. If set to <code>soap12</code>, the SOAP1.2 interface will + be triggered. See <a href="#soap12format">below for the SOAP 1.2 response format description</a>.</td> + <td>unset</td> +</tr> +<tr> + <th>charset</th> + <td>Character encoding override: + Specify the character encoding to use when parsing the document. When used with + the auxiliary parameter <code>fbc</code> set to 1, the given encoding will only be used as + a fallback value, in case the charset is absent or unrecognized. Note that this parameter + is ignored if validating a <code>fragment</code> with the direct input interface.</td> + <td>None, by default the validator detects the charset of the document automatically.</td> +</tr> +<tr> + <th>doctype</th> + <td>Document Type override: + Specify the Document Type (DOCTYPE) to use when parsing the document. When used + with the auxiliary parameter <code>fbd</code> set to 1, the given document type will only be used + as a fallback value, in case the document's DOCTYPE declaration is missing or unrecognized.</td> + <td>None, by default the validator detects the document type of the document automatically.</td> +</tr> +<tr> + <th>verbose</th> + <td>In the web interface, when set to 1, will make error messages, explanations + and other diagnostics more verbose. In SOAP output, does not have any impact.</td> + <td>0 (unset)</td> +</tr> +<tr> + <th>ss</th> + <td> as <em>show source</em>. In the web interface, triggers the display of the source + after the validation results. In SOAP output, does not have any impact.</td> + <td>0 (unset)</td> +</tr> +<tr> + <th>outline</th> + <td>In the web interface, when set to 1, triggers the display of the document outline + after the validation results. In SOAP output, does not have any impact.</td> + <td>0 (unset)</td> +</tr> +</tbody></table> +<h3 id="soap12format">SOAP format description</h3> + +<p>When called with parameter <code>output=soap12</code>, the validator will switch +to its SOAP 1.2 interface (experimental for now). Below is a sample response, as well as +a description of the most important elements of the response.</p> + +<h4 id="soap12_sample">sample SOAP 1.2 validation response</h4> +<p>A SOAP response for the validation of a document (invalid) will look like this:</p> +<pre style="font-size: smaller;"> +<?xml version="1.0" encoding="UTF-8"?> +<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> +<env:Body> +<<a href="#soap12_markupvalidationresponse">m:markupvalidationresponse</a> +env:encodingStyle="http://www.w3.org/2003/05/soap-encoding" +xmlns:m="http://www.w3.org/2005/10/markup-validator"> + <<a href="#soap12_uri">m:uri</a>>http://qa-dev.w3.org/wmvs/HEAD/dev/tests/xhtml1-bogus-element.html</m:uri> + <<a href="#soap12_checkedby">m:checkedby</a>>http://validator.w3.org/</m:checkedby> + <<a href="#soap12_doctype">m:doctype</a>>-//W3C//DTD XHTML 1.0 Transitional//EN</m:doctype> + <<a href="#soap12_charset">m:charset</a>>utf-8</m:charset> + <<a href="#soap12_validity">m:validity</a>>false</m:validity> + <<a href="#soap12_errors">m:errors</a>> + <<a href="#soap12_errorcount">m:errorcount</a>>1</m:errorcount> + <<a href="#soap12_errorlist">m:errorlist</a>> + + <<a href="#soap12_error">m:error</a>> + <<a href="#soap12_line">m:line</a>>13</m:line> + <<a href="#soap12_col">m:col</a>>6</m:col> + <<a href="#soap12_message">m:message</a>>element "foo" undefined</m:message> + </m:error> + + </m:errorlist> + </m:errors> + <m:warnings> + <m:warningcount>0</m:warningcount> + <m:warninglist> + + + </m:warninglist> + </m:warnings> +</m:markupvalidationresponse> +</env:Body> +</env:Envelope> +</pre> +<h4 id="soap12response">SOAP1.2 response format reference</h4> + +<table class="refdoc"> +<tbody><tr><th>element</th><th>description</th></tr> +<tr> + <th id="soap12_markupvalidationresponse">markupvalidationresponse</th> + <td>The main element of the validation response. Encloses all other information about the validation results.</td> +</tr> +<tr> + <th id="soap12_uri">uri</th> + <td>the address of the document validated. Will (likely?) be <kbd>upload://Form Submission</kbd> + if an uploaded document or fragment was validated. + In <a href="http://www.w3.org/WAI/ER/">EARL</a> terms, this is the <kbd>TestSubject</kbd>. + </td> +</tr> +<tr> + <th id="soap12_checkedby">checkedby</th> + <td>Location of the service which provided the validation result. + In <a href="http://www.w3.org/WAI/ER/">EARL</a> terms, this is the <kbd>Assertor</kbd>. + </td> +</tr> +<tr> + <th id="soap12_doctype">doctype</th> + <td>Detected (or forced) Document Type for the validated document</td> +</tr> +<tr> + <th id="soap12_charset">charset</th> + <td>Detected (or forced) Character Encoding for the validated document</td> +</tr> +<tr> + <th id="soap12_validity">validity</th> + <td>Whether or not the document validated passed or not formal validation (true|false boolean)</td> +</tr> +<tr> + <th id="soap12_errors">errors</th> + <td>Encapsulates all data about errors encountered through the validation process</td> +</tr> +<tr> + <th id="soap12_errorcount">errorcount</th> + <td>a child of <a href="#soap12_errors">errors</a>, counts the number of errors listed</td> +</tr> +<tr> + <th id="soap12_errorlist">errorlist</th> + <td>a child of <a href="#soap12_errors">errors</a>, contains the list of errors (surprise!)</td> +</tr> +<tr> + <th id="soap12_error">error</th> + <td>a child of <a href="#soap12_errorlist">errorlist</a>, contains the information on a single + validation error. </td> +</tr> +</tbody></table> +<p><strong>Note</strong>: <code>warnings</code>, <code>warningcount</code>, +<code>warninglist</code> and <code>warning</code> are similar to, respectively, +<code><a href="#soap12_errors">errors</a></code>, +<code><a href="#soap12_errorcount">errorcount</a></code>, +<code><a href="#soap12_errorlist">errorlist</a></code> and +<code><a href="#soap12_error">error</a></code>. +</p> +<h4 id="soap12message">SOAP1.2 atomic message (error or warning) format reference</h4> +<p>As seen as the example above, the children of the <code><a href="#soap12_error">error</a></code> +element, but also the <code>warning</code> element are <code>line</code>, <code>col</code> and +<code>message</code>, defined below:</p> +<table class="refdoc"> +<tbody><tr><th>element</th><th>description</th></tr> +<tr> + <th id="soap12_line">line</th> + <td>Within the source code of the validated document, refers to the line where the error was detected.</td> +</tr> +<tr> + <th id="soap12_col">col</th> + <td>Within the source code of the validated document, refers to the line where the column was detected.</td> +</tr> +<tr> + <th id="soap12_message">message</th> + <td>The actual error message</td> +</tr> +<tr> + <th id="soap12_messageid">messageid</th> + <td>@@ not implemented yet@@ - should be the number of the error, as addressed internally by the validator</td> +</tr> +</tbody></table> + +<h3 id="libs">Libraries</h3> +<p>Building of libraries used to interact with the validator's API is <a href="http://www.w3.org/QA/2006/10/validator_api.html">encouraged</a>. If you are the + maintainer of such a library, <a href="http://validator.w3.org/feedback.html">contact us</a> and we will list it here.</p> +<h4>Known libraries for the W3C Markup Validator API</h4> + +<ul> + <li><a href="http://search.cpan.org/dist/WebService-Validator-HTML-W3C/">WebService::Validator::HTML::W3C</a> in perl, + by Struan Donald.</li> +</ul> + +<h3 id="http_headers">Using HTTP headers to know validation results</h3> + +<p>Every validation result is served via the HTTP protocol, with custom headers giving a simple, quick way + to get validation results without having to parse the results body. This is a simple (but poorer) alternative to using + the full API described above.</p> + +<p>The HTTP headers for a validation results page will generally look like:</p> +<pre> HEAD 'http://validator.localhost/check?uri=http%3A%2F%2Fwww.w3.org' + + 200 OK + [...] + Content-Language: en + Content-Type: text/html; charset=utf-8 + X-W3C-Validator-Errors: 0 + X-W3C-Validator-Recursion: 1 + X-W3C-Validator-Status: Valid +</pre> + +<p>The headers and their values are as follows:</p> +<table> + <tbody><tr><th>Header</th><th>Value</th><th>Notes</th></tr> + <tr> + <td>X-W3C-Validator-Status</td> + <td><code>Valid</code> or <code>Invalid</code></td> + <td>May not be present if validation could not be performed (404 Not found, etc)</td> + </tr> + <tr> + <td>X-W3C-Validator-Errors</td> + <td>Number of Errors found during validation. <code>0</code> if no errors found.</td> + <td>0 does not necessarily mean "valid" (it may mean that validation could not be performed)</td> + </tr> + <tr> + <td>X-W3C-Validator-Recursion</td> + <td>Integer. Generally, <code>1</code>. More if recursively validating validation results. + </td> + <td> The validator will use this in conjunction with its <code>Max Recursion</code> setup to avoid + abusive recursion (Denial of Service attack).</td> + </tr> +</tbody></table> +</div> + </div><!-- End of "main" DIV. --> + +<address> + <a href="http://validator.w3.org/check?uri=referer"><img src="api_files/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88"></a> + Feedback: + <a title="Send Feedback for the W3C Markup Validation Service" href="http://validator.w3.org/feedback.html">The W3C Validator Team</a><br> + <!-- SSI Template Version: $Id$ --> + </address> + <p class="copyright"> + <a rel="Copyright" href="http://www.w3.org/Consortium/Legal/ipr-notice#Copyright">Copyright</a> © 1994-2006 + <a href="http://www.w3.org/"><acronym title="World Wide Web Consortium">W3C</acronym></a>® + (<a href="http://www.csail.mit.edu/"><acronym title="Massachusetts Institute of Technology">MIT</acronym></a>, + <a href="http://www.ercim.org/"><acronym title="European Research Consortium for Informatics and Mathematics">ERCIM</acronym></a>, + <a href="http://www.keio.ac.jp/">Keio</a>), + All Rights Reserved. + W3C <a href="http://www.w3.org/Consortium/Legal/ipr-notice#Legal_Disclaimer">liability</a>, + <a href="http://www.w3.org/Consortium/Legal/ipr-notice#W3C_Trademarks">trademark</a>, + <a rel="Copyright" href="http://www.w3.org/Consortium/Legal/copyright-documents">document use</a> + and <a rel="Copyright" href="http://www.w3.org/Consortium/Legal/copyright-software">software licensing</a> + rules apply. Your interactions with this site are in accordance + with our <a href="http://www.w3.org/Consortium/Legal/privacy-statement#Public">public</a> and + <a href="http://www.w3.org/Consortium/Legal/privacy-statement#Members">Member</a> privacy + statements. + </p> + + </body></html> \ No newline at end of file diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_byfile.php b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_byfile.php new file mode 100644 index 0000000000000000000000000000000000000000..a9c4be0ca1a9ef348e41f74ee1f6fc42eeb5f7ed --- /dev/null +++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_byfile.php @@ -0,0 +1,26 @@ +<?php +/** + * Example usage of Services_W3C_HTMLValidator of validating a local file. + * + * PHP versions 5 + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @version CVS: $id$ + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ + +error_reporting(E_ALL); +ini_set('display_errors', true); +require_once 'Services/W3C/HTMLValidator.php'; + +$v = new Services_W3C_HTMLValidator(); + +echo '<pre>'; +var_dump($v->validateFile('example.html')); + + +?> \ No newline at end of file diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_byuri.php b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_byuri.php new file mode 100644 index 0000000000000000000000000000000000000000..d34e7823fedda4d30606473676e163d52c177932 --- /dev/null +++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_byuri.php @@ -0,0 +1,26 @@ +<?php +/** + * Example file to demonstrate validating a URI. + * + * PHP versions 5 + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @version CVS: $id$ + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ + +error_reporting(E_ALL); +ini_set('display_errors', true); +require_once 'Services/W3C/HTMLValidator.php'; + +$v = new Services_W3C_HTMLValidator(); + +echo '<pre>'; +var_dump($v->validate('http://www.unl.edu/')); + + +?> \ No newline at end of file diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_fragment.php b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_fragment.php new file mode 100644 index 0000000000000000000000000000000000000000..00248f739afc819d617703e3644bee833a89471e --- /dev/null +++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_fragment.php @@ -0,0 +1,30 @@ +<?php +/** + * Example file demonstrating how to validate an HTML fragment. + * + * PHP versions 5 + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @version CVS: $id$ + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ + +error_reporting(E_ALL); +ini_set('display_errors', true); +require_once 'Services/W3C/HTMLValidator.php'; + +$v = new Services_W3C_HTMLValidator(); + +echo '<pre>'; +$t = $v->validateFragment('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" +"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> + <head><title>HTML Fragment</title></head> + <body><p>TEST!</p></body> +</html>'); +var_dump($r); +?> \ No newline at end of file diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_responsexml.php b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_responsexml.php new file mode 100644 index 0000000000000000000000000000000000000000..8747de3ef9e8115f2d3b81f6ac60ffdec1dfc624 --- /dev/null +++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_responsexml.php @@ -0,0 +1,57 @@ +<?php +/** + * Example file which shows how to validate a raw SOAP12 response. + * + * PHP versions 5 + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @version CVS: $id$ + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ + +error_reporting(E_ALL); +ini_set('display_errors', true); +require_once 'Services/W3C/HTMLValidator.php'; + +$v = new Services_W3C_HTMLValidator(); + +echo '<pre>'; +var_dump($v->parseSOAP12Response('<?xml version="1.0" encoding="UTF-8"?> +<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> +<env:Body> +<m:markupvalidationresponse +env:encodingStyle="http://www.w3.org/2003/05/soap-encoding" +xmlns:m="http://www.w3.org/2005/10/markup-validator"> + <m:uri>http://qa-dev.w3.org/wmvs/HEAD/dev/tests/xhtml1-bogus-element.html</m:uri> + <m:checkedby>http://validator.w3.org/</m:checkedby> + <m:doctype>-//W3C//DTD XHTML 1.0 Transitional//EN</m:doctype> + <m:charset>utf-8</m:charset> + <m:validity>false</m:validity> + <m:errors> + <m:errorcount>1</m:errorcount> + <m:errorlist> + + <m:error> + <m:line>13</m:line> + <m:col>6</m:col> + <m:message>element "foo" undefined</m:message> + </m:error> + + </m:errorlist> + </m:errors> + <m:warnings> + <m:warningcount>0</m:warningcount> + <m:warninglist> + + + </m:warninglist> + </m:warnings> +</m:markupvalidationresponse> +</env:Body> +</env:Envelope> +')); +?> \ No newline at end of file diff --git a/lib/docs/Spider/pearhub.org/examples/basic.php b/lib/docs/Spider/pearhub.org/examples/basic.php new file mode 100644 index 0000000000000000000000000000000000000000..d62330266c85a5f641638a4c9a00b9af6ed5e3bc --- /dev/null +++ b/lib/docs/Spider/pearhub.org/examples/basic.php @@ -0,0 +1,52 @@ +<?php + +function autoload($class) +{ + $class = str_replace('_', '/', $class); + if (file_exists(dirname(__FILE__) . '/../src/' . $class . '.php')) { + include dirname(__FILE__) . '/../src/' . $class . '.php'; + } +} + +spl_autoload_register("autoload"); + +unlink('results.db'); + +$dsn = 'sqlite:results.db'; +$db = new PDO($dsn); +$db->exec('create table SpiderPage ( + id serial, + uri varchar(255), + primary key(id) +);'); +$db->exec( +'create table SpiderJavaScript ( + id serial, + uri varchar(255), + script varchar(255), + primary key(id) +);'); + +$db->exec('create table SpiderStyleSheet ( + id serial, + uri varchar(255), + style varchar(255), + primary key(id) +);'); +//$pageLogger = new Spider_PageLogger($db); +//$javaScriptLogger = new Spider_JavaScriptLogger($db); +//$styleSheetLogger = new Spider_StyleSheetLogger($db); +$logger = new Spider_Logger(); +$downloader = new Spider_Downloader(); +$parser = new Spider_Parser(); +$spider = new Spider($downloader, $parser); + +$spider->addLogger($logger); +$spider->addUriFilter('Spider_AnchorFilter'); +$spider->addUriFilter('Spider_MailtoFilter'); +//$spider->addLogger($pageLogger); +//$spider->addLogger($styleSheetLogger); +//$spider->addLogger($javaScriptLogger); + + +$spider->spider('http://www.unl.edu/fwc/'); diff --git a/lib/docs/Spider/pearhub.org/examples/batch_validator.php b/lib/docs/Spider/pearhub.org/examples/batch_validator.php new file mode 100644 index 0000000000000000000000000000000000000000..2fc29dfcdb71c535b1b65240b711f41873702aa6 --- /dev/null +++ b/lib/docs/Spider/pearhub.org/examples/batch_validator.php @@ -0,0 +1,172 @@ +<?php +function autoload($class) +{ + $class = str_replace('_', '/', $class); + if (file_exists(dirname(__FILE__) . '/../src/' . $class . '.php')) { + include dirname(__FILE__) . '/../src/' . $class . '.php'; + } +} + +spl_autoload_register("autoload"); + +class ValidationLogger extends Spider_LoggerAbstract +{ + public $validator; + + public $page; + + function __construct($validator, $page) + { + $this->validator = $validator; + $this->page = $page; + } + + function log($uri, DOMXPath $xpath) + { + + $this->page->maincontentarea .= '<h1>'.htmlentities($uri).'</h1>'; + +// $r = $this->validator->validate($uri); +// $this->displayValidationDetails($r); + } + + function displayValidationDetails($r) + { + if ($r->isValid()) { + $this->page->maincontentarea .= '<h2 class="results" class="valid">This page is Valid!</h2>'; + $this->page->maincontentarea .= 'Passed validation, '.count($r->warnings).' Warnings'; + } else { + $this->page->maincontentarea .= '<h2 class="results" class="invalid">This page is <strong>not</strong> Valid!</h2>'; + $this->page->maincontentarea .= 'Failed validation, '.count($r->errors).' Errors'; + } + $this->page->maincontentarea .= '<div class="results_container">'; + if (count($r->warnings)) { + $this->page->maincontentarea .= '<h3 class="preparse_warnings">Important Warnings</h3> + <p>The validator has found the following problem(s) prior to validation, + which should be addressed in priority:</p>'; + $this->page->maincontentarea .= '<ol class="warnings">'; + addMessageSection($r->warnings, 'warn', $this->page); + $this->page->maincontentarea .= '</ol>'; + } + if (count($r->errors)) { + $this->page->maincontentarea .= ' + <h3 class="invalid">Validation Output: '.count($r->errors).' Errors</h3>'; + $this->page->maincontentarea .= '<ol class="error_loop">'; + addMessageSection($r->errors, 'err', $this->page); + $this->page->maincontentarea .= '</ol>'; + } + $this->page->maincontentarea .= '</div>'; + } +} + +class FileExtensionFilter extends Spider_UriFilterInterface +{ + function accept() + { + $path_parts = pathinfo($this->current()); + if (!isset($path_parts['extension']) + || $path_parts['extension'] == 'html' + || $path_parts['extension'] == 'php' + || $path_parts['extension'] == 'shtml' + || $path_parts['extension'] == 'asp' + || $path_parts['extension'] == 'aspx' + || $path_parts['extension'] == 'jsp') { + return true; + } + return false; + } +} + +/** + * Example file demonstrating a user interface for validating pages. + * + * PHP versions 5 + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @version CVS: $id$ + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ + +error_reporting(E_ALL); +ini_set('display_errors', true); +require_once 'Services/W3C/HTMLValidator.php'; +require_once 'UNL/Templates.php'; + +UNL_Templates::$options['version'] = 3; + +const VALIDATOR_URI = 'http://validator.unl.edu/check'; + +$v = new Services_W3C_HTMLValidator(); +$v->validator_uri = VALIDATOR_URI; + +$p = UNL_Templates::factory('Document'); +$p->titlegraphic = '<h1>UNL W3C Validator Instance</h1><h2>Hey validator, rock n roll. Rock on.</h2>'; +$p->head .= '<style type="text/css" media="all">@import "'.substr($v->validator_uri,0,-5).'/style/results.css";</style>'; +$p->navlinks = '<ul><li><a href="http://www.unl.edu/wdn/">WDN</a></li></ul>'; +$p->maincontentarea = ''; + +$uri = ''; + +if (isset($_GET['uri'])) { + $uri = htmlentities($_GET['uri'], ENT_QUOTES); +} + +$p->maincontentarea .= '<form id="form" method="get" action=""> + <table class="header"> + <tbody> + <tr> + <th><label title="Address of Page to Validate" for="uri">Address</label>:</th> + <td colspan="2"><input id="uri" name="uri" value="'.$uri.'" size="50" type="text" /></td> + </tr> + </tbody></table> + <fieldset id="revalidate_opts"> + <div id="revalidate_button" class="submit_button"><input value="Revalidate" title="Validate this document again" type="submit" /></div> + </fieldset> +</form>'; + +function addMessageSection($messages, $mtype, &$p) +{ + foreach ($messages as $message) { + addMessage($message, $mtype, $p); + } +} + +function addMessage($message, $mtype, &$p) +{ + $p->maincontentarea .= ' + <li class="msg_'.$mtype.'"> + <span class="err_type">'; + if ($mtype == 'err') { + $p->maincontentarea .= '<img src="'.substr(VALIDATOR_URI, 0, -5).'/images/info_icons/error.png" alt="Error" title="Error" />'; + } else { + $p->maincontentarea .= '<img src="'.substr(VALIDATOR_URI, 0, -5).'/images/info_icons/warning.png" alt="Warning" title="Warning" />'; + } + $p->maincontentarea .= '</span> + <em>Line '.$message->line.', Column '.$message->col.'</em>: + <span class="msg">'.htmlentities($message->message).'</span>.<pre><code class="input">'.$message->source.'</code></pre>'; + $p->maincontentarea .= $message->explanation; + $p->maincontentarea .= ' + </li>'; +} + +if (isset($_GET['uri'])) { + $logger = new ValidationLogger($v, $p); + $downloader = new Spider_Downloader(); + $parser = new Spider_Parser(); + $spider = new Spider($downloader, $parser); + + $spider->addLogger($logger); + $spider->addUriFilter('Spider_AnchorFilter'); + $spider->addUriFilter('Spider_MailtoFilter'); + $spider->addUriFilter('FileExtensionFilter'); + + $spider->spider($_GET['uri']); +} + +echo $p->toHtml(); + +?> diff --git a/lib/docs/Spider/pearhub.org/examples/results.db b/lib/docs/Spider/pearhub.org/examples/results.db new file mode 100644 index 0000000000000000000000000000000000000000..b934c03407ced1ac7d6d4dfabe196c898692485b Binary files /dev/null and b/lib/docs/Spider/pearhub.org/examples/results.db differ diff --git a/lib/downloads/HTTP_Request2-0.5.1.tgz b/lib/downloads/HTTP_Request2-0.5.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..1fb02a30c70ae63705bdeaf26bfc1aed1980fafc Binary files /dev/null and b/lib/downloads/HTTP_Request2-0.5.1.tgz differ diff --git a/lib/downloads/Net_URL2-0.3.0.tgz b/lib/downloads/Net_URL2-0.3.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..19d38a7154681a396327aeb570b25566592aa7a7 Binary files /dev/null and b/lib/downloads/Net_URL2-0.3.0.tgz differ diff --git a/lib/downloads/Services_W3C_HTMLValidator-1.0.0RC2.tgz b/lib/downloads/Services_W3C_HTMLValidator-1.0.0RC2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..702b85379c7e58ae3b9c0660625c34ac1c826eac Binary files /dev/null and b/lib/downloads/Services_W3C_HTMLValidator-1.0.0RC2.tgz differ diff --git a/lib/php/HTTP/Request2.php b/lib/php/HTTP/Request2.php new file mode 100644 index 0000000000000000000000000000000000000000..19dfe66a9e58e2b21c9cb8e1b1e2e70fc1075b16 --- /dev/null +++ b/lib/php/HTTP/Request2.php @@ -0,0 +1,861 @@ +<?php +/** + * Class representing a HTTP request message + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: Request2.php 290921 2009-11-18 17:31:58Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * A class representing an URL as per RFC 3986. + */ +require_once 'Net/URL2.php'; + +/** + * Exception class for HTTP_Request2 package + */ +require_once 'HTTP/Request2/Exception.php'; + +/** + * Class representing a HTTP request message + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @version Release: 0.5.1 + * @link http://tools.ietf.org/html/rfc2616#section-5 + */ +class HTTP_Request2 implements SplSubject +{ + /**#@+ + * Constants for HTTP request methods + * + * @link http://tools.ietf.org/html/rfc2616#section-5.1.1 + */ + const METHOD_OPTIONS = 'OPTIONS'; + const METHOD_GET = 'GET'; + const METHOD_HEAD = 'HEAD'; + const METHOD_POST = 'POST'; + const METHOD_PUT = 'PUT'; + const METHOD_DELETE = 'DELETE'; + const METHOD_TRACE = 'TRACE'; + const METHOD_CONNECT = 'CONNECT'; + /**#@-*/ + + /**#@+ + * Constants for HTTP authentication schemes + * + * @link http://tools.ietf.org/html/rfc2617 + */ + const AUTH_BASIC = 'basic'; + const AUTH_DIGEST = 'digest'; + /**#@-*/ + + /** + * Regular expression used to check for invalid symbols in RFC 2616 tokens + * @link http://pear.php.net/bugs/bug.php?id=15630 + */ + const REGEXP_INVALID_TOKEN = '![\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]!'; + + /** + * Regular expression used to check for invalid symbols in cookie strings + * @link http://pear.php.net/bugs/bug.php?id=15630 + * @link http://web.archive.org/web/20080331104521/http://cgi.netscape.com/newsref/std/cookie_spec.html + */ + const REGEXP_INVALID_COOKIE = '/[\s,;]/'; + + /** + * Fileinfo magic database resource + * @var resource + * @see detectMimeType() + */ + private static $_fileinfoDb; + + /** + * Observers attached to the request (instances of SplObserver) + * @var array + */ + protected $observers = array(); + + /** + * Request URL + * @var Net_URL2 + */ + protected $url; + + /** + * Request method + * @var string + */ + protected $method = self::METHOD_GET; + + /** + * Authentication data + * @var array + * @see getAuth() + */ + protected $auth; + + /** + * Request headers + * @var array + */ + protected $headers = array(); + + /** + * Configuration parameters + * @var array + * @see setConfig() + */ + protected $config = array( + 'adapter' => 'HTTP_Request2_Adapter_Socket', + 'connect_timeout' => 10, + 'timeout' => 0, + 'use_brackets' => true, + 'protocol_version' => '1.1', + 'buffer_size' => 16384, + 'store_body' => true, + + 'proxy_host' => '', + 'proxy_port' => '', + 'proxy_user' => '', + 'proxy_password' => '', + 'proxy_auth_scheme' => self::AUTH_BASIC, + + 'ssl_verify_peer' => true, + 'ssl_verify_host' => true, + 'ssl_cafile' => null, + 'ssl_capath' => null, + 'ssl_local_cert' => null, + 'ssl_passphrase' => null, + + 'digest_compat_ie' => false, + + 'follow_redirects' => false, + 'max_redirects' => 5, + 'strict_redirects' => false + ); + + /** + * Last event in request / response handling, intended for observers + * @var array + * @see getLastEvent() + */ + protected $lastEvent = array( + 'name' => 'start', + 'data' => null + ); + + /** + * Request body + * @var string|resource + * @see setBody() + */ + protected $body = ''; + + /** + * Array of POST parameters + * @var array + */ + protected $postParams = array(); + + /** + * Array of file uploads (for multipart/form-data POST requests) + * @var array + */ + protected $uploads = array(); + + /** + * Adapter used to perform actual HTTP request + * @var HTTP_Request2_Adapter + */ + protected $adapter; + + + /** + * Constructor. Can set request URL, method and configuration array. + * + * Also sets a default value for User-Agent header. + * + * @param string|Net_Url2 Request URL + * @param string Request method + * @param array Configuration for this Request instance + */ + public function __construct($url = null, $method = self::METHOD_GET, array $config = array()) + { + $this->setConfig($config); + if (!empty($url)) { + $this->setUrl($url); + } + if (!empty($method)) { + $this->setMethod($method); + } + $this->setHeader('user-agent', 'HTTP_Request2/0.5.1 ' . + '(http://pear.php.net/package/http_request2) ' . + 'PHP/' . phpversion()); + } + + /** + * Sets the URL for this request + * + * If the URL has userinfo part (username & password) these will be removed + * and converted to auth data. If the URL does not have a path component, + * that will be set to '/'. + * + * @param string|Net_URL2 Request URL + * @return HTTP_Request2 + * @throws HTTP_Request2_Exception + */ + public function setUrl($url) + { + if (is_string($url)) { + $url = new Net_URL2( + $url, array(Net_URL2::OPTION_USE_BRACKETS => $this->config['use_brackets']) + ); + } + if (!$url instanceof Net_URL2) { + throw new HTTP_Request2_Exception('Parameter is not a valid HTTP URL'); + } + // URL contains username / password? + if ($url->getUserinfo()) { + $username = $url->getUser(); + $password = $url->getPassword(); + $this->setAuth(rawurldecode($username), $password? rawurldecode($password): ''); + $url->setUserinfo(''); + } + if ('' == $url->getPath()) { + $url->setPath('/'); + } + $this->url = $url; + + return $this; + } + + /** + * Returns the request URL + * + * @return Net_URL2 + */ + public function getUrl() + { + return $this->url; + } + + /** + * Sets the request method + * + * @param string + * @return HTTP_Request2 + * @throws HTTP_Request2_Exception if the method name is invalid + */ + public function setMethod($method) + { + // Method name should be a token: http://tools.ietf.org/html/rfc2616#section-5.1.1 + if (preg_match(self::REGEXP_INVALID_TOKEN, $method)) { + throw new HTTP_Request2_Exception("Invalid request method '{$method}'"); + } + $this->method = $method; + + return $this; + } + + /** + * Returns the request method + * + * @return string + */ + public function getMethod() + { + return $this->method; + } + + /** + * Sets the configuration parameter(s) + * + * The following parameters are available: + * <ul> + * <li> 'adapter' - adapter to use (string)</li> + * <li> 'connect_timeout' - Connection timeout in seconds (integer)</li> + * <li> 'timeout' - Total number of seconds a request can take. + * Use 0 for no limit, should be greater than + * 'connect_timeout' if set (integer)</li> + * <li> 'use_brackets' - Whether to append [] to array variable names (bool)</li> + * <li> 'protocol_version' - HTTP Version to use, '1.0' or '1.1' (string)</li> + * <li> 'buffer_size' - Buffer size to use for reading and writing (int)</li> + * <li> 'store_body' - Whether to store response body in response object. + * Set to false if receiving a huge response and + * using an Observer to save it (boolean)</li> + * <li> 'proxy_host' - Proxy server host (string)</li> + * <li> 'proxy_port' - Proxy server port (integer)</li> + * <li> 'proxy_user' - Proxy auth username (string)</li> + * <li> 'proxy_password' - Proxy auth password (string)</li> + * <li> 'proxy_auth_scheme' - Proxy auth scheme, one of HTTP_Request2::AUTH_* constants (string)</li> + * <li> 'ssl_verify_peer' - Whether to verify peer's SSL certificate (bool)</li> + * <li> 'ssl_verify_host' - Whether to check that Common Name in SSL + * certificate matches host name (bool)</li> + * <li> 'ssl_cafile' - Cerificate Authority file to verify the peer + * with (use with 'ssl_verify_peer') (string)</li> + * <li> 'ssl_capath' - Directory holding multiple Certificate + * Authority files (string)</li> + * <li> 'ssl_local_cert' - Name of a file containing local cerificate (string)</li> + * <li> 'ssl_passphrase' - Passphrase with which local certificate + * was encoded (string)</li> + * <li> 'digest_compat_ie' - Whether to imitate behaviour of MSIE 5 and 6 + * in using URL without query string in digest + * authentication (boolean)</li> + * <li> 'follow_redirects' - Whether to automatically follow HTTP Redirects (boolean)</li> + * <li> 'max_redirects' - Maximum number of redirects to follow (integer)</li> + * <li> 'strict_redirects' - Whether to keep request method on redirects via status 301 and + * 302 (true, needed for compatibility with RFC 2616) + * or switch to GET (false, needed for compatibility with most + * browsers) (boolean)</li> + * </ul> + * + * @param string|array configuration parameter name or array + * ('parameter name' => 'parameter value') + * @param mixed parameter value if $nameOrConfig is not an array + * @return HTTP_Request2 + * @throws HTTP_Request2_Exception If the parameter is unknown + */ + public function setConfig($nameOrConfig, $value = null) + { + if (is_array($nameOrConfig)) { + foreach ($nameOrConfig as $name => $value) { + $this->setConfig($name, $value); + } + + } else { + if (!array_key_exists($nameOrConfig, $this->config)) { + throw new HTTP_Request2_Exception( + "Unknown configuration parameter '{$nameOrConfig}'" + ); + } + $this->config[$nameOrConfig] = $value; + } + + return $this; + } + + /** + * Returns the value(s) of the configuration parameter(s) + * + * @param string parameter name + * @return mixed value of $name parameter, array of all configuration + * parameters if $name is not given + * @throws HTTP_Request2_Exception If the parameter is unknown + */ + public function getConfig($name = null) + { + if (null === $name) { + return $this->config; + } elseif (!array_key_exists($name, $this->config)) { + throw new HTTP_Request2_Exception( + "Unknown configuration parameter '{$name}'" + ); + } + return $this->config[$name]; + } + + /** + * Sets the autentification data + * + * @param string user name + * @param string password + * @param string authentication scheme + * @return HTTP_Request2 + */ + public function setAuth($user, $password = '', $scheme = self::AUTH_BASIC) + { + if (empty($user)) { + $this->auth = null; + } else { + $this->auth = array( + 'user' => (string)$user, + 'password' => (string)$password, + 'scheme' => $scheme + ); + } + + return $this; + } + + /** + * Returns the authentication data + * + * The array has the keys 'user', 'password' and 'scheme', where 'scheme' + * is one of the HTTP_Request2::AUTH_* constants. + * + * @return array + */ + public function getAuth() + { + return $this->auth; + } + + /** + * Sets request header(s) + * + * The first parameter may be either a full header string 'header: value' or + * header name. In the former case $value parameter is ignored, in the latter + * the header's value will either be set to $value or the header will be + * removed if $value is null. The first parameter can also be an array of + * headers, in that case method will be called recursively. + * + * Note that headers are treated case insensitively as per RFC 2616. + * + * <code> + * $req->setHeader('Foo: Bar'); // sets the value of 'Foo' header to 'Bar' + * $req->setHeader('FoO', 'Baz'); // sets the value of 'Foo' header to 'Baz' + * $req->setHeader(array('foo' => 'Quux')); // sets the value of 'Foo' header to 'Quux' + * $req->setHeader('FOO'); // removes 'Foo' header from request + * </code> + * + * @param string|array header name, header string ('Header: value') + * or an array of headers + * @param string|null header value, header will be removed if null + * @return HTTP_Request2 + * @throws HTTP_Request2_Exception + */ + public function setHeader($name, $value = null) + { + if (is_array($name)) { + foreach ($name as $k => $v) { + if (is_string($k)) { + $this->setHeader($k, $v); + } else { + $this->setHeader($v); + } + } + } else { + if (null === $value && strpos($name, ':')) { + list($name, $value) = array_map('trim', explode(':', $name, 2)); + } + // Header name should be a token: http://tools.ietf.org/html/rfc2616#section-4.2 + if (preg_match(self::REGEXP_INVALID_TOKEN, $name)) { + throw new HTTP_Request2_Exception("Invalid header name '{$name}'"); + } + // Header names are case insensitive anyway + $name = strtolower($name); + if (null === $value) { + unset($this->headers[$name]); + } else { + $this->headers[$name] = $value; + } + } + + return $this; + } + + /** + * Returns the request headers + * + * The array is of the form ('header name' => 'header value'), header names + * are lowercased + * + * @return array + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * Appends a cookie to "Cookie:" header + * + * @param string cookie name + * @param string cookie value + * @return HTTP_Request2 + * @throws HTTP_Request2_Exception + */ + public function addCookie($name, $value) + { + $cookie = $name . '=' . $value; + if (preg_match(self::REGEXP_INVALID_COOKIE, $cookie)) { + throw new HTTP_Request2_Exception("Invalid cookie: '{$cookie}'"); + } + $cookies = empty($this->headers['cookie'])? '': $this->headers['cookie'] . '; '; + $this->setHeader('cookie', $cookies . $cookie); + + return $this; + } + + /** + * Sets the request body + * + * @param string Either a string with the body or filename containing body + * @param bool Whether first parameter is a filename + * @return HTTP_Request2 + * @throws HTTP_Request2_Exception + */ + public function setBody($body, $isFilename = false) + { + if (!$isFilename) { + if (!$body instanceof HTTP_Request2_MultipartBody) { + $this->body = (string)$body; + } else { + $this->body = $body; + } + } else { + if (!($fp = @fopen($body, 'rb'))) { + throw new HTTP_Request2_Exception("Cannot open file {$body}"); + } + $this->body = $fp; + if (empty($this->headers['content-type'])) { + $this->setHeader('content-type', self::detectMimeType($body)); + } + } + $this->postParams = $this->uploads = array(); + + return $this; + } + + /** + * Returns the request body + * + * @return string|resource|HTTP_Request2_MultipartBody + */ + public function getBody() + { + if (self::METHOD_POST == $this->method && + (!empty($this->postParams) || !empty($this->uploads)) + ) { + if ('application/x-www-form-urlencoded' == $this->headers['content-type']) { + $body = http_build_query($this->postParams, '', '&'); + if (!$this->getConfig('use_brackets')) { + $body = preg_replace('/%5B\d+%5D=/', '=', $body); + } + // support RFC 3986 by not encoding '~' symbol (request #15368) + return str_replace('%7E', '~', $body); + + } elseif ('multipart/form-data' == $this->headers['content-type']) { + require_once 'HTTP/Request2/MultipartBody.php'; + return new HTTP_Request2_MultipartBody( + $this->postParams, $this->uploads, $this->getConfig('use_brackets') + ); + } + } + return $this->body; + } + + /** + * Adds a file to form-based file upload + * + * Used to emulate file upload via a HTML form. The method also sets + * Content-Type of HTTP request to 'multipart/form-data'. + * + * If you just want to send the contents of a file as the body of HTTP + * request you should use setBody() method. + * + * @param string name of file-upload field + * @param mixed full name of local file + * @param string filename to send in the request + * @param string content-type of file being uploaded + * @return HTTP_Request2 + * @throws HTTP_Request2_Exception + */ + public function addUpload($fieldName, $filename, $sendFilename = null, + $contentType = null) + { + if (!is_array($filename)) { + if (!($fp = @fopen($filename, 'rb'))) { + throw new HTTP_Request2_Exception("Cannot open file {$filename}"); + } + $this->uploads[$fieldName] = array( + 'fp' => $fp, + 'filename' => empty($sendFilename)? basename($filename): $sendFilename, + 'size' => filesize($filename), + 'type' => empty($contentType)? self::detectMimeType($filename): $contentType + ); + } else { + $fps = $names = $sizes = $types = array(); + foreach ($filename as $f) { + if (!is_array($f)) { + $f = array($f); + } + if (!($fp = @fopen($f[0], 'rb'))) { + throw new HTTP_Request2_Exception("Cannot open file {$f[0]}"); + } + $fps[] = $fp; + $names[] = empty($f[1])? basename($f[0]): $f[1]; + $sizes[] = filesize($f[0]); + $types[] = empty($f[2])? self::detectMimeType($f[0]): $f[2]; + } + $this->uploads[$fieldName] = array( + 'fp' => $fps, 'filename' => $names, 'size' => $sizes, 'type' => $types + ); + } + if (empty($this->headers['content-type']) || + 'application/x-www-form-urlencoded' == $this->headers['content-type'] + ) { + $this->setHeader('content-type', 'multipart/form-data'); + } + + return $this; + } + + /** + * Adds POST parameter(s) to the request. + * + * @param string|array parameter name or array ('name' => 'value') + * @param mixed parameter value (can be an array) + * @return HTTP_Request2 + */ + public function addPostParameter($name, $value = null) + { + if (!is_array($name)) { + $this->postParams[$name] = $value; + } else { + foreach ($name as $k => $v) { + $this->addPostParameter($k, $v); + } + } + if (empty($this->headers['content-type'])) { + $this->setHeader('content-type', 'application/x-www-form-urlencoded'); + } + + return $this; + } + + /** + * Attaches a new observer + * + * @param SplObserver + */ + public function attach(SplObserver $observer) + { + foreach ($this->observers as $attached) { + if ($attached === $observer) { + return; + } + } + $this->observers[] = $observer; + } + + /** + * Detaches an existing observer + * + * @param SplObserver + */ + public function detach(SplObserver $observer) + { + foreach ($this->observers as $key => $attached) { + if ($attached === $observer) { + unset($this->observers[$key]); + return; + } + } + } + + /** + * Notifies all observers + */ + public function notify() + { + foreach ($this->observers as $observer) { + $observer->update($this); + } + } + + /** + * Sets the last event + * + * Adapters should use this method to set the current state of the request + * and notify the observers. + * + * @param string event name + * @param mixed event data + */ + public function setLastEvent($name, $data = null) + { + $this->lastEvent = array( + 'name' => $name, + 'data' => $data + ); + $this->notify(); + } + + /** + * Returns the last event + * + * Observers should use this method to access the last change in request. + * The following event names are possible: + * <ul> + * <li>'connect' - after connection to remote server, + * data is the destination (string)</li> + * <li>'disconnect' - after disconnection from server</li> + * <li>'sentHeaders' - after sending the request headers, + * data is the headers sent (string)</li> + * <li>'sentBodyPart' - after sending a part of the request body, + * data is the length of that part (int)</li> + * <li>'receivedHeaders' - after receiving the response headers, + * data is HTTP_Request2_Response object</li> + * <li>'receivedBodyPart' - after receiving a part of the response + * body, data is that part (string)</li> + * <li>'receivedEncodedBodyPart' - as 'receivedBodyPart', but data is still + * encoded by Content-Encoding</li> + * <li>'receivedBody' - after receiving the complete response + * body, data is HTTP_Request2_Response object</li> + * </ul> + * Different adapters may not send all the event types. Mock adapter does + * not send any events to the observers. + * + * @return array The array has two keys: 'name' and 'data' + */ + public function getLastEvent() + { + return $this->lastEvent; + } + + /** + * Sets the adapter used to actually perform the request + * + * You can pass either an instance of a class implementing HTTP_Request2_Adapter + * or a class name. The method will only try to include a file if the class + * name starts with HTTP_Request2_Adapter_, it will also try to prepend this + * prefix to the class name if it doesn't contain any underscores, so that + * <code> + * $request->setAdapter('curl'); + * </code> + * will work. + * + * @param string|HTTP_Request2_Adapter + * @return HTTP_Request2 + * @throws HTTP_Request2_Exception + */ + public function setAdapter($adapter) + { + if (is_string($adapter)) { + if (!class_exists($adapter, false)) { + if (false === strpos($adapter, '_')) { + $adapter = 'HTTP_Request2_Adapter_' . ucfirst($adapter); + } + if (preg_match('/^HTTP_Request2_Adapter_([a-zA-Z0-9]+)$/', $adapter)) { + include_once str_replace('_', DIRECTORY_SEPARATOR, $adapter) . '.php'; + } + if (!class_exists($adapter, false)) { + throw new HTTP_Request2_Exception("Class {$adapter} not found"); + } + } + $adapter = new $adapter; + } + if (!$adapter instanceof HTTP_Request2_Adapter) { + throw new HTTP_Request2_Exception('Parameter is not a HTTP request adapter'); + } + $this->adapter = $adapter; + + return $this; + } + + /** + * Sends the request and returns the response + * + * @throws HTTP_Request2_Exception + * @return HTTP_Request2_Response + */ + public function send() + { + // Sanity check for URL + if (!$this->url instanceof Net_URL2) { + throw new HTTP_Request2_Exception('No URL given'); + } elseif (!$this->url->isAbsolute()) { + throw new HTTP_Request2_Exception('Absolute URL required'); + } elseif (!in_array(strtolower($this->url->getScheme()), array('https', 'http'))) { + throw new HTTP_Request2_Exception('Not a HTTP URL'); + } + if (empty($this->adapter)) { + $this->setAdapter($this->getConfig('adapter')); + } + // magic_quotes_runtime may break file uploads and chunked response + // processing; see bug #4543 + if ($magicQuotes = ini_get('magic_quotes_runtime')) { + ini_set('magic_quotes_runtime', false); + } + // force using single byte encoding if mbstring extension overloads + // strlen() and substr(); see bug #1781, bug #10605 + if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) { + $oldEncoding = mb_internal_encoding(); + mb_internal_encoding('iso-8859-1'); + } + + try { + $response = $this->adapter->sendRequest($this); + } catch (Exception $e) { + } + // cleanup in either case (poor man's "finally" clause) + if ($magicQuotes) { + ini_set('magic_quotes_runtime', true); + } + if (!empty($oldEncoding)) { + mb_internal_encoding($oldEncoding); + } + // rethrow the exception + if (!empty($e)) { + throw $e; + } + return $response; + } + + /** + * Tries to detect MIME type of a file + * + * The method will try to use fileinfo extension if it is available, + * deprecated mime_content_type() function in the other case. If neither + * works, default 'application/octet-stream' MIME type is returned + * + * @param string filename + * @return string file MIME type + */ + protected static function detectMimeType($filename) + { + // finfo extension from PECL available + if (function_exists('finfo_open')) { + if (!isset(self::$_fileinfoDb)) { + self::$_fileinfoDb = @finfo_open(FILEINFO_MIME); + } + if (self::$_fileinfoDb) { + $info = finfo_file(self::$_fileinfoDb, $filename); + } + } + // (deprecated) mime_content_type function available + if (empty($info) && function_exists('mime_content_type')) { + return mime_content_type($filename); + } + return empty($info)? 'application/octet-stream': $info; + } +} +?> \ No newline at end of file diff --git a/lib/php/HTTP/Request2/Adapter.php b/lib/php/HTTP/Request2/Adapter.php new file mode 100644 index 0000000000000000000000000000000000000000..16ff131bd155a0bb596caa0d0756adddfcbed7d0 --- /dev/null +++ b/lib/php/HTTP/Request2/Adapter.php @@ -0,0 +1,154 @@ +<?php +/** + * Base class for HTTP_Request2 adapters + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: Adapter.php 291118 2009-11-21 17:58:23Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Class representing a HTTP response + */ +require_once 'HTTP/Request2/Response.php'; + +/** + * Base class for HTTP_Request2 adapters + * + * HTTP_Request2 class itself only defines methods for aggregating the request + * data, all actual work of sending the request to the remote server and + * receiving its response is performed by adapters. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @version Release: 0.5.1 + */ +abstract class HTTP_Request2_Adapter +{ + /** + * A list of methods that MUST NOT have a request body, per RFC 2616 + * @var array + */ + protected static $bodyDisallowed = array('TRACE'); + + /** + * Methods having defined semantics for request body + * + * Content-Length header (indicating that the body follows, section 4.3 of + * RFC 2616) will be sent for these methods even if no body was added + * + * @var array + * @link http://pear.php.net/bugs/bug.php?id=12900 + * @link http://pear.php.net/bugs/bug.php?id=14740 + */ + protected static $bodyRequired = array('POST', 'PUT'); + + /** + * Request being sent + * @var HTTP_Request2 + */ + protected $request; + + /** + * Request body + * @var string|resource|HTTP_Request2_MultipartBody + * @see HTTP_Request2::getBody() + */ + protected $requestBody; + + /** + * Length of the request body + * @var integer + */ + protected $contentLength; + + /** + * Sends request to the remote server and returns its response + * + * @param HTTP_Request2 + * @return HTTP_Request2_Response + * @throws HTTP_Request2_Exception + */ + abstract public function sendRequest(HTTP_Request2 $request); + + /** + * Calculates length of the request body, adds proper headers + * + * @param array associative array of request headers, this method will + * add proper 'Content-Length' and 'Content-Type' headers + * to this array (or remove them if not needed) + */ + protected function calculateRequestLength(&$headers) + { + $this->requestBody = $this->request->getBody(); + + if (is_string($this->requestBody)) { + $this->contentLength = strlen($this->requestBody); + } elseif (is_resource($this->requestBody)) { + $stat = fstat($this->requestBody); + $this->contentLength = $stat['size']; + rewind($this->requestBody); + } else { + $this->contentLength = $this->requestBody->getLength(); + $headers['content-type'] = 'multipart/form-data; boundary=' . + $this->requestBody->getBoundary(); + $this->requestBody->rewind(); + } + + if (in_array($this->request->getMethod(), self::$bodyDisallowed) || + 0 == $this->contentLength + ) { + // No body: send a Content-Length header nonetheless (request #12900), + // but do that only for methods that require a body (bug #14740) + if (in_array($this->request->getMethod(), self::$bodyRequired)) { + $headers['content-length'] = 0; + } else { + unset($headers['content-length']); + // if the method doesn't require a body and doesn't have a + // body, don't send a Content-Type header. (request #16799) + unset($headers['content-type']); + } + } else { + if (empty($headers['content-type'])) { + $headers['content-type'] = 'application/x-www-form-urlencoded'; + } + $headers['content-length'] = $this->contentLength; + } + } +} +?> diff --git a/lib/php/HTTP/Request2/Adapter/Curl.php b/lib/php/HTTP/Request2/Adapter/Curl.php new file mode 100644 index 0000000000000000000000000000000000000000..77ab6fae02b026e6944c71ec663dcbb7c5cf5988 --- /dev/null +++ b/lib/php/HTTP/Request2/Adapter/Curl.php @@ -0,0 +1,461 @@ +<?php +/** + * Adapter for HTTP_Request2 wrapping around cURL extension + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: Curl.php 291118 2009-11-21 17:58:23Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Base class for HTTP_Request2 adapters + */ +require_once 'HTTP/Request2/Adapter.php'; + +/** + * Adapter for HTTP_Request2 wrapping around cURL extension + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @version Release: 0.5.1 + */ +class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter +{ + /** + * Mapping of header names to cURL options + * @var array + */ + protected static $headerMap = array( + 'accept-encoding' => CURLOPT_ENCODING, + 'cookie' => CURLOPT_COOKIE, + 'referer' => CURLOPT_REFERER, + 'user-agent' => CURLOPT_USERAGENT + ); + + /** + * Mapping of SSL context options to cURL options + * @var array + */ + protected static $sslContextMap = array( + 'ssl_verify_peer' => CURLOPT_SSL_VERIFYPEER, + 'ssl_cafile' => CURLOPT_CAINFO, + 'ssl_capath' => CURLOPT_CAPATH, + 'ssl_local_cert' => CURLOPT_SSLCERT, + 'ssl_passphrase' => CURLOPT_SSLCERTPASSWD + ); + + /** + * Response being received + * @var HTTP_Request2_Response + */ + protected $response; + + /** + * Whether 'sentHeaders' event was sent to observers + * @var boolean + */ + protected $eventSentHeaders = false; + + /** + * Whether 'receivedHeaders' event was sent to observers + * @var boolean + */ + protected $eventReceivedHeaders = false; + + /** + * Position within request body + * @var integer + * @see callbackReadBody() + */ + protected $position = 0; + + /** + * Information about last transfer, as returned by curl_getinfo() + * @var array + */ + protected $lastInfo; + + /** + * Sends request to the remote server and returns its response + * + * @param HTTP_Request2 + * @return HTTP_Request2_Response + * @throws HTTP_Request2_Exception + */ + public function sendRequest(HTTP_Request2 $request) + { + if (!extension_loaded('curl')) { + throw new HTTP_Request2_Exception('cURL extension not available'); + } + + $this->request = $request; + $this->response = null; + $this->position = 0; + $this->eventSentHeaders = false; + $this->eventReceivedHeaders = false; + + try { + if (false === curl_exec($ch = $this->createCurlHandle())) { + $errorMessage = 'Error sending request: #' . curl_errno($ch) . + ' ' . curl_error($ch); + } + } catch (Exception $e) { + } + $this->lastInfo = curl_getinfo($ch); + curl_close($ch); + + $response = $this->response; + unset($this->request, $this->requestBody, $this->response); + + if (!empty($e)) { + throw $e; + } elseif (!empty($errorMessage)) { + throw new HTTP_Request2_Exception($errorMessage); + } + + if (0 < $this->lastInfo['size_download']) { + $request->setLastEvent('receivedBody', $response); + } + return $response; + } + + /** + * Returns information about last transfer + * + * @return array associative array as returned by curl_getinfo() + */ + public function getInfo() + { + return $this->lastInfo; + } + + /** + * Creates a new cURL handle and populates it with data from the request + * + * @return resource a cURL handle, as created by curl_init() + * @throws HTTP_Request2_Exception + */ + protected function createCurlHandle() + { + $ch = curl_init(); + + curl_setopt_array($ch, array( + // setup write callbacks + CURLOPT_HEADERFUNCTION => array($this, 'callbackWriteHeader'), + CURLOPT_WRITEFUNCTION => array($this, 'callbackWriteBody'), + // buffer size + CURLOPT_BUFFERSIZE => $this->request->getConfig('buffer_size'), + // connection timeout + CURLOPT_CONNECTTIMEOUT => $this->request->getConfig('connect_timeout'), + // save full outgoing headers, in case someone is interested + CURLINFO_HEADER_OUT => true, + // request url + CURLOPT_URL => $this->request->getUrl()->getUrl() + )); + + // set up redirects + if (!$this->request->getConfig('follow_redirects')) { + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); + } else { + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_MAXREDIRS, $this->request->getConfig('max_redirects')); + // limit redirects to http(s), works in 5.2.10+ + if (defined('CURLOPT_REDIR_PROTOCOLS')) { + curl_setopt($ch, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + } + // works sometime after 5.3.0, http://bugs.php.net/bug.php?id=49571 + if ($this->request->getConfig('strict_redirects') && defined('CURLOPT_POSTREDIR ')) { + curl_setopt($ch, CURLOPT_POSTREDIR, 3); + } + } + + // request timeout + if ($timeout = $this->request->getConfig('timeout')) { + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + } + + // set HTTP version + switch ($this->request->getConfig('protocol_version')) { + case '1.0': + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + break; + case '1.1': + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + } + + // set request method + switch ($this->request->getMethod()) { + case HTTP_Request2::METHOD_GET: + curl_setopt($ch, CURLOPT_HTTPGET, true); + break; + case HTTP_Request2::METHOD_POST: + curl_setopt($ch, CURLOPT_POST, true); + break; + case HTTP_Request2::METHOD_HEAD: + curl_setopt($ch, CURLOPT_NOBODY, true); + break; + default: + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->request->getMethod()); + } + + // set proxy, if needed + if ($host = $this->request->getConfig('proxy_host')) { + if (!($port = $this->request->getConfig('proxy_port'))) { + throw new HTTP_Request2_Exception('Proxy port not provided'); + } + curl_setopt($ch, CURLOPT_PROXY, $host . ':' . $port); + if ($user = $this->request->getConfig('proxy_user')) { + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $user . ':' . + $this->request->getConfig('proxy_password')); + switch ($this->request->getConfig('proxy_auth_scheme')) { + case HTTP_Request2::AUTH_BASIC: + curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); + break; + case HTTP_Request2::AUTH_DIGEST: + curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST); + } + } + } + + // set authentication data + if ($auth = $this->request->getAuth()) { + curl_setopt($ch, CURLOPT_USERPWD, $auth['user'] . ':' . $auth['password']); + switch ($auth['scheme']) { + case HTTP_Request2::AUTH_BASIC: + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + break; + case HTTP_Request2::AUTH_DIGEST: + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + } + } + + // set SSL options + if (0 == strcasecmp($this->request->getUrl()->getScheme(), 'https')) { + foreach ($this->request->getConfig() as $name => $value) { + if ('ssl_verify_host' == $name && null !== $value) { + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $value? 2: 0); + } elseif (isset(self::$sslContextMap[$name]) && null !== $value) { + curl_setopt($ch, self::$sslContextMap[$name], $value); + } + } + } + + $headers = $this->request->getHeaders(); + // make cURL automagically send proper header + if (!isset($headers['accept-encoding'])) { + $headers['accept-encoding'] = ''; + } + + // set headers having special cURL keys + foreach (self::$headerMap as $name => $option) { + if (isset($headers[$name])) { + curl_setopt($ch, $option, $headers[$name]); + unset($headers[$name]); + } + } + + $this->calculateRequestLength($headers); + if (isset($headers['content-length'])) { + $this->workaroundPhpBug47204($ch, $headers); + } + + // set headers not having special keys + $headersFmt = array(); + foreach ($headers as $name => $value) { + $canonicalName = implode('-', array_map('ucfirst', explode('-', $name))); + $headersFmt[] = $canonicalName . ': ' . $value; + } + curl_setopt($ch, CURLOPT_HTTPHEADER, $headersFmt); + + return $ch; + } + + /** + * Workaround for PHP bug #47204 that prevents rewinding request body + * + * The workaround consists of reading the entire request body into memory + * and setting it as CURLOPT_POSTFIELDS, so it isn't recommended for large + * file uploads, use Socket adapter instead. + * + * @param resource cURL handle + * @param array Request headers + */ + protected function workaroundPhpBug47204($ch, &$headers) + { + // no redirects, no digest auth -> probably no rewind needed + if (!$this->request->getConfig('follow_redirects') + && (!($auth = $this->request->getAuth()) + || HTTP_Request2::AUTH_DIGEST != $auth['scheme']) + ) { + curl_setopt($ch, CURLOPT_READFUNCTION, array($this, 'callbackReadBody')); + + // rewind may be needed, read the whole body into memory + } else { + if ($this->requestBody instanceof HTTP_Request2_MultipartBody) { + $this->requestBody = $this->requestBody->__toString(); + + } elseif (is_resource($this->requestBody)) { + $fp = $this->requestBody; + $this->requestBody = ''; + while (!feof($fp)) { + $this->requestBody .= fread($fp, 16384); + } + } + // curl hangs up if content-length is present + unset($headers['content-length']); + curl_setopt($ch, CURLOPT_POSTFIELDS, $this->requestBody); + } + } + + /** + * Callback function called by cURL for reading the request body + * + * @param resource cURL handle + * @param resource file descriptor (not used) + * @param integer maximum length of data to return + * @return string part of the request body, up to $length bytes + */ + protected function callbackReadBody($ch, $fd, $length) + { + if (!$this->eventSentHeaders) { + $this->request->setLastEvent( + 'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT) + ); + $this->eventSentHeaders = true; + } + if (in_array($this->request->getMethod(), self::$bodyDisallowed) || + 0 == $this->contentLength || $this->position >= $this->contentLength + ) { + return ''; + } + if (is_string($this->requestBody)) { + $string = substr($this->requestBody, $this->position, $length); + } elseif (is_resource($this->requestBody)) { + $string = fread($this->requestBody, $length); + } else { + $string = $this->requestBody->read($length); + } + $this->request->setLastEvent('sentBodyPart', strlen($string)); + $this->position += strlen($string); + return $string; + } + + /** + * Callback function called by cURL for saving the response headers + * + * @param resource cURL handle + * @param string response header (with trailing CRLF) + * @return integer number of bytes saved + * @see HTTP_Request2_Response::parseHeaderLine() + */ + protected function callbackWriteHeader($ch, $string) + { + // we may receive a second set of headers if doing e.g. digest auth + if ($this->eventReceivedHeaders || !$this->eventSentHeaders) { + // don't bother with 100-Continue responses (bug #15785) + if (!$this->eventSentHeaders || + $this->response->getStatus() >= 200 + ) { + $this->request->setLastEvent( + 'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT) + ); + } + $upload = curl_getinfo($ch, CURLINFO_SIZE_UPLOAD); + // if body wasn't read by a callback, send event with total body size + if ($upload > $this->position) { + $this->request->setLastEvent( + 'sentBodyPart', $upload - $this->position + ); + $this->position = $upload; + } + $this->eventSentHeaders = true; + // we'll need a new response object + if ($this->eventReceivedHeaders) { + $this->eventReceivedHeaders = false; + $this->response = null; + } + } + if (empty($this->response)) { + $this->response = new HTTP_Request2_Response($string, false); + } else { + $this->response->parseHeaderLine($string); + if ('' == trim($string)) { + // don't bother with 100-Continue responses (bug #15785) + if (200 <= $this->response->getStatus()) { + $this->request->setLastEvent('receivedHeaders', $this->response); + } + // for versions lower than 5.2.10, check the redirection URL protocol + if ($this->request->getConfig('follow_redirects') && !defined('CURLOPT_REDIR_PROTOCOLS') + && $this->response->isRedirect() + ) { + $redirectUrl = new Net_URL2($this->response->getHeader('location')); + if ($redirectUrl->isAbsolute() + && !in_array($redirectUrl->getScheme(), array('http', 'https')) + ) { + return -1; + } + } + $this->eventReceivedHeaders = true; + } + } + return strlen($string); + } + + /** + * Callback function called by cURL for saving the response body + * + * @param resource cURL handle (not used) + * @param string part of the response body + * @return integer number of bytes saved + * @see HTTP_Request2_Response::appendBody() + */ + protected function callbackWriteBody($ch, $string) + { + // cURL calls WRITEFUNCTION callback without calling HEADERFUNCTION if + // response doesn't start with proper HTTP status line (see bug #15716) + if (empty($this->response)) { + throw new HTTP_Request2_Exception("Malformed response: {$string}"); + } + if ($this->request->getConfig('store_body')) { + $this->response->appendBody($string); + } + $this->request->setLastEvent('receivedBodyPart', $string); + return strlen($string); + } +} +?> diff --git a/lib/php/HTTP/Request2/Adapter/Mock.php b/lib/php/HTTP/Request2/Adapter/Mock.php new file mode 100644 index 0000000000000000000000000000000000000000..2812ce93ea84b972a7a7230cf25cd3d97aef97a2 --- /dev/null +++ b/lib/php/HTTP/Request2/Adapter/Mock.php @@ -0,0 +1,171 @@ +<?php +/** + * Mock adapter intended for testing + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: Mock.php 290192 2009-11-03 21:29:32Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Base class for HTTP_Request2 adapters + */ +require_once 'HTTP/Request2/Adapter.php'; + +/** + * Mock adapter intended for testing + * + * Can be used to test applications depending on HTTP_Request2 package without + * actually performing any HTTP requests. This adapter will return responses + * previously added via addResponse() + * <code> + * $mock = new HTTP_Request2_Adapter_Mock(); + * $mock->addResponse("HTTP/1.1 ... "); + * + * $request = new HTTP_Request2(); + * $request->setAdapter($mock); + * + * // This will return the response set above + * $response = $req->send(); + * </code> + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @version Release: 0.5.1 + */ +class HTTP_Request2_Adapter_Mock extends HTTP_Request2_Adapter +{ + /** + * A queue of responses to be returned by sendRequest() + * @var array + */ + protected $responses = array(); + + /** + * Returns the next response from the queue built by addResponse() + * + * If the queue is empty it will return default empty response with status 400, + * if an Exception object was added to the queue it will be thrown. + * + * @param HTTP_Request2 + * @return HTTP_Request2_Response + * @throws Exception + */ + public function sendRequest(HTTP_Request2 $request) + { + if (count($this->responses) > 0) { + $response = array_shift($this->responses); + if ($response instanceof HTTP_Request2_Response) { + return $response; + } else { + // rethrow the exception + $class = get_class($response); + $message = $response->getMessage(); + $code = $response->getCode(); + throw new $class($message, $code); + } + } else { + return self::createResponseFromString("HTTP/1.1 400 Bad Request\r\n\r\n"); + } + } + + /** + * Adds response to the queue + * + * @param mixed either a string, a pointer to an open file, + * an instance of HTTP_Request2_Response or Exception + * @throws HTTP_Request2_Exception + */ + public function addResponse($response) + { + if (is_string($response)) { + $response = self::createResponseFromString($response); + } elseif (is_resource($response)) { + $response = self::createResponseFromFile($response); + } elseif (!$response instanceof HTTP_Request2_Response && + !$response instanceof Exception + ) { + throw new HTTP_Request2_Exception('Parameter is not a valid response'); + } + $this->responses[] = $response; + } + + /** + * Creates a new HTTP_Request2_Response object from a string + * + * @param string + * @return HTTP_Request2_Response + * @throws HTTP_Request2_Exception + */ + public static function createResponseFromString($str) + { + $parts = preg_split('!(\r?\n){2}!m', $str, 2); + $headerLines = explode("\n", $parts[0]); + $response = new HTTP_Request2_Response(array_shift($headerLines)); + foreach ($headerLines as $headerLine) { + $response->parseHeaderLine($headerLine); + } + $response->parseHeaderLine(''); + if (isset($parts[1])) { + $response->appendBody($parts[1]); + } + return $response; + } + + /** + * Creates a new HTTP_Request2_Response object from a file + * + * @param resource file pointer returned by fopen() + * @return HTTP_Request2_Response + * @throws HTTP_Request2_Exception + */ + public static function createResponseFromFile($fp) + { + $response = new HTTP_Request2_Response(fgets($fp)); + do { + $headerLine = fgets($fp); + $response->parseHeaderLine($headerLine); + } while ('' != trim($headerLine)); + + while (!feof($fp)) { + $response->appendBody(fread($fp, 8192)); + } + return $response; + } +} +?> \ No newline at end of file diff --git a/lib/php/HTTP/Request2/Adapter/Socket.php b/lib/php/HTTP/Request2/Adapter/Socket.php new file mode 100644 index 0000000000000000000000000000000000000000..6d1788c7d4f7a46465221b67a3c35094a29de30f --- /dev/null +++ b/lib/php/HTTP/Request2/Adapter/Socket.php @@ -0,0 +1,1046 @@ +<?php +/** + * Socket-based adapter for HTTP_Request2 + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: Socket.php 290921 2009-11-18 17:31:58Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Base class for HTTP_Request2 adapters + */ +require_once 'HTTP/Request2/Adapter.php'; + +/** + * Socket-based adapter for HTTP_Request2 + * + * This adapter uses only PHP sockets and will work on almost any PHP + * environment. Code is based on original HTTP_Request PEAR package. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @version Release: 0.5.1 + */ +class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter +{ + /** + * Regular expression for 'token' rule from RFC 2616 + */ + const REGEXP_TOKEN = '[^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+'; + + /** + * Regular expression for 'quoted-string' rule from RFC 2616 + */ + const REGEXP_QUOTED_STRING = '"(?:\\\\.|[^\\\\"])*"'; + + /** + * Connected sockets, needed for Keep-Alive support + * @var array + * @see connect() + */ + protected static $sockets = array(); + + /** + * Data for digest authentication scheme + * + * The keys for the array are URL prefixes. + * + * The values are associative arrays with data (realm, nonce, nonce-count, + * opaque...) needed for digest authentication. Stored here to prevent making + * duplicate requests to digest-protected resources after we have already + * received the challenge. + * + * @var array + */ + protected static $challenges = array(); + + /** + * Connected socket + * @var resource + * @see connect() + */ + protected $socket; + + /** + * Challenge used for server digest authentication + * @var array + */ + protected $serverChallenge; + + /** + * Challenge used for proxy digest authentication + * @var array + */ + protected $proxyChallenge; + + /** + * Sum of start time and global timeout, exception will be thrown if request continues past this time + * @var integer + */ + protected $deadline = null; + + /** + * Remaining length of the current chunk, when reading chunked response + * @var integer + * @see readChunked() + */ + protected $chunkLength = 0; + + /** + * Remaining amount of redirections to follow + * + * Starts at 'max_redirects' configuration parameter and is reduced on each + * subsequent redirect. An Exception will be thrown once it reaches zero. + * + * @var integer + */ + protected $redirectCountdown = null; + + /** + * Sends request to the remote server and returns its response + * + * @param HTTP_Request2 + * @return HTTP_Request2_Response + * @throws HTTP_Request2_Exception + */ + public function sendRequest(HTTP_Request2 $request) + { + $this->request = $request; + + // Use global request timeout if given, see feature requests #5735, #8964 + if ($timeout = $request->getConfig('timeout')) { + $this->deadline = time() + $timeout; + } else { + $this->deadline = null; + } + + try { + $keepAlive = $this->connect(); + $headers = $this->prepareHeaders(); + if (false === @fwrite($this->socket, $headers, strlen($headers))) { + throw new HTTP_Request2_Exception('Error writing request'); + } + // provide request headers to the observer, see request #7633 + $this->request->setLastEvent('sentHeaders', $headers); + $this->writeBody(); + + if ($this->deadline && time() > $this->deadline) { + throw new HTTP_Request2_Exception( + 'Request timed out after ' . + $request->getConfig('timeout') . ' second(s)' + ); + } + + $response = $this->readResponse(); + + if (!$this->canKeepAlive($keepAlive, $response)) { + $this->disconnect(); + } + + if ($this->shouldUseProxyDigestAuth($response)) { + return $this->sendRequest($request); + } + if ($this->shouldUseServerDigestAuth($response)) { + return $this->sendRequest($request); + } + if ($authInfo = $response->getHeader('authentication-info')) { + $this->updateChallenge($this->serverChallenge, $authInfo); + } + if ($proxyInfo = $response->getHeader('proxy-authentication-info')) { + $this->updateChallenge($this->proxyChallenge, $proxyInfo); + } + + } catch (Exception $e) { + $this->disconnect(); + } + + unset($this->request, $this->requestBody); + + if (!empty($e)) { + throw $e; + } + + if (!$request->getConfig('follow_redirects') || !$response->isRedirect()) { + return $response; + } else { + return $this->handleRedirect($request, $response); + } + } + + /** + * Connects to the remote server + * + * @return bool whether the connection can be persistent + * @throws HTTP_Request2_Exception + */ + protected function connect() + { + $secure = 0 == strcasecmp($this->request->getUrl()->getScheme(), 'https'); + $tunnel = HTTP_Request2::METHOD_CONNECT == $this->request->getMethod(); + $headers = $this->request->getHeaders(); + $reqHost = $this->request->getUrl()->getHost(); + if (!($reqPort = $this->request->getUrl()->getPort())) { + $reqPort = $secure? 443: 80; + } + + if ($host = $this->request->getConfig('proxy_host')) { + if (!($port = $this->request->getConfig('proxy_port'))) { + throw new HTTP_Request2_Exception('Proxy port not provided'); + } + $proxy = true; + } else { + $host = $reqHost; + $port = $reqPort; + $proxy = false; + } + + if ($tunnel && !$proxy) { + throw new HTTP_Request2_Exception( + "Trying to perform CONNECT request without proxy" + ); + } + if ($secure && !in_array('ssl', stream_get_transports())) { + throw new HTTP_Request2_Exception( + 'Need OpenSSL support for https:// requests' + ); + } + + // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive + // connection token to a proxy server... + if ($proxy && !$secure && + !empty($headers['connection']) && 'Keep-Alive' == $headers['connection'] + ) { + $this->request->setHeader('connection'); + } + + $keepAlive = ('1.1' == $this->request->getConfig('protocol_version') && + empty($headers['connection'])) || + (!empty($headers['connection']) && + 'Keep-Alive' == $headers['connection']); + $host = ((!$secure || $proxy)? 'tcp://': 'ssl://') . $host; + + $options = array(); + if ($secure || $tunnel) { + foreach ($this->request->getConfig() as $name => $value) { + if ('ssl_' == substr($name, 0, 4) && null !== $value) { + if ('ssl_verify_host' == $name) { + if ($value) { + $options['CN_match'] = $reqHost; + } + } else { + $options[substr($name, 4)] = $value; + } + } + } + ksort($options); + } + + // Changing SSL context options after connection is established does *not* + // work, we need a new connection if options change + $remote = $host . ':' . $port; + $socketKey = $remote . (($secure && $proxy)? "->{$reqHost}:{$reqPort}": '') . + (empty($options)? '': ':' . serialize($options)); + unset($this->socket); + + // We use persistent connections and have a connected socket? + // Ensure that the socket is still connected, see bug #16149 + if ($keepAlive && !empty(self::$sockets[$socketKey]) && + !feof(self::$sockets[$socketKey]) + ) { + $this->socket =& self::$sockets[$socketKey]; + + } elseif ($secure && $proxy && !$tunnel) { + $this->establishTunnel(); + $this->request->setLastEvent( + 'connect', "ssl://{$reqHost}:{$reqPort} via {$host}:{$port}" + ); + self::$sockets[$socketKey] =& $this->socket; + + } else { + // Set SSL context options if doing HTTPS request or creating a tunnel + $context = stream_context_create(); + foreach ($options as $name => $value) { + if (!stream_context_set_option($context, 'ssl', $name, $value)) { + throw new HTTP_Request2_Exception( + "Error setting SSL context option '{$name}'" + ); + } + } + $this->socket = @stream_socket_client( + $remote, $errno, $errstr, + $this->request->getConfig('connect_timeout'), + STREAM_CLIENT_CONNECT, $context + ); + if (!$this->socket) { + throw new HTTP_Request2_Exception( + "Unable to connect to {$remote}. Error #{$errno}: {$errstr}" + ); + } + $this->request->setLastEvent('connect', $remote); + self::$sockets[$socketKey] =& $this->socket; + } + return $keepAlive; + } + + /** + * Establishes a tunnel to a secure remote server via HTTP CONNECT request + * + * This method will fail if 'ssl_verify_peer' is enabled. Probably because PHP + * sees that we are connected to a proxy server (duh!) rather than the server + * that presents its certificate. + * + * @link http://tools.ietf.org/html/rfc2817#section-5.2 + * @throws HTTP_Request2_Exception + */ + protected function establishTunnel() + { + $donor = new self; + $connect = new HTTP_Request2( + $this->request->getUrl(), HTTP_Request2::METHOD_CONNECT, + array_merge($this->request->getConfig(), + array('adapter' => $donor)) + ); + $response = $connect->send(); + // Need any successful (2XX) response + if (200 > $response->getStatus() || 300 <= $response->getStatus()) { + throw new HTTP_Request2_Exception( + 'Failed to connect via HTTPS proxy. Proxy response: ' . + $response->getStatus() . ' ' . $response->getReasonPhrase() + ); + } + $this->socket = $donor->socket; + + $modes = array( + STREAM_CRYPTO_METHOD_TLS_CLIENT, + STREAM_CRYPTO_METHOD_SSLv3_CLIENT, + STREAM_CRYPTO_METHOD_SSLv23_CLIENT, + STREAM_CRYPTO_METHOD_SSLv2_CLIENT + ); + + foreach ($modes as $mode) { + if (stream_socket_enable_crypto($this->socket, true, $mode)) { + return; + } + } + throw new HTTP_Request2_Exception( + 'Failed to enable secure connection when connecting through proxy' + ); + } + + /** + * Checks whether current connection may be reused or should be closed + * + * @param boolean whether connection could be persistent + * in the first place + * @param HTTP_Request2_Response response object to check + * @return boolean + */ + protected function canKeepAlive($requestKeepAlive, HTTP_Request2_Response $response) + { + // Do not close socket on successful CONNECT request + if (HTTP_Request2::METHOD_CONNECT == $this->request->getMethod() && + 200 <= $response->getStatus() && 300 > $response->getStatus() + ) { + return true; + } + + $lengthKnown = 'chunked' == strtolower($response->getHeader('transfer-encoding')) || + null !== $response->getHeader('content-length'); + $persistent = 'keep-alive' == strtolower($response->getHeader('connection')) || + (null === $response->getHeader('connection') && + '1.1' == $response->getVersion()); + return $requestKeepAlive && $lengthKnown && $persistent; + } + + /** + * Disconnects from the remote server + */ + protected function disconnect() + { + if (is_resource($this->socket)) { + fclose($this->socket); + $this->socket = null; + $this->request->setLastEvent('disconnect'); + } + } + + /** + * Handles HTTP redirection + * + * This method will throw an Exception if redirect to a non-HTTP(S) location + * is attempted, also if number of redirects performed already is equal to + * 'max_redirects' configuration parameter. + * + * @param HTTP_Request2 Original request + * @param HTTP_Request2_Response Response containing redirect + * @return HTTP_Request2_Response Response from a new location + * @throws HTTP_Request2_Exception + */ + protected function handleRedirect(HTTP_Request2 $request, + HTTP_Request2_Response $response) + { + if (is_null($this->redirectCountdown)) { + $this->redirectCountdown = $request->getConfig('max_redirects'); + } + if (0 == $this->redirectCountdown) { + // Copying cURL behaviour + throw new HTTP_Request2_Exception( + 'Maximum (' . $request->getConfig('max_redirects') . ') redirects followed' + ); + } + $redirectUrl = new Net_URL2( + $response->getHeader('location'), + array(Net_URL2::OPTION_USE_BRACKETS => $request->getConfig('use_brackets')) + ); + // refuse non-HTTP redirect + if ($redirectUrl->isAbsolute() + && !in_array($redirectUrl->getScheme(), array('http', 'https')) + ) { + throw new HTTP_Request2_Exception( + 'Refusing to redirect to a non-HTTP URL ' . $redirectUrl->__toString() + ); + } + // Theoretically URL should be absolute (see http://tools.ietf.org/html/rfc2616#section-14.30), + // but in practice it is often not + if (!$redirectUrl->isAbsolute()) { + $redirectUrl = $request->getUrl()->resolve($redirectUrl); + } + $redirect = clone $request; + $redirect->setUrl($redirectUrl); + if (303 == $response->getStatus() || (!$request->getConfig('strict_redirects') + && in_array($response->getStatus(), array(301, 302))) + ) { + $redirect->setMethod(HTTP_Request2::METHOD_GET); + $redirect->setBody(''); + } + + if (0 < $this->redirectCountdown) { + $this->redirectCountdown--; + } + return $this->sendRequest($redirect); + } + + /** + * Checks whether another request should be performed with server digest auth + * + * Several conditions should be satisfied for it to return true: + * - response status should be 401 + * - auth credentials should be set in the request object + * - response should contain WWW-Authenticate header with digest challenge + * - there is either no challenge stored for this URL or new challenge + * contains stale=true parameter (in other case we probably just failed + * due to invalid username / password) + * + * The method stores challenge values in $challenges static property + * + * @param HTTP_Request2_Response response to check + * @return boolean whether another request should be performed + * @throws HTTP_Request2_Exception in case of unsupported challenge parameters + */ + protected function shouldUseServerDigestAuth(HTTP_Request2_Response $response) + { + // no sense repeating a request if we don't have credentials + if (401 != $response->getStatus() || !$this->request->getAuth()) { + return false; + } + if (!$challenge = $this->parseDigestChallenge($response->getHeader('www-authenticate'))) { + return false; + } + + $url = $this->request->getUrl(); + $scheme = $url->getScheme(); + $host = $scheme . '://' . $url->getHost(); + if ($port = $url->getPort()) { + if ((0 == strcasecmp($scheme, 'http') && 80 != $port) || + (0 == strcasecmp($scheme, 'https') && 443 != $port) + ) { + $host .= ':' . $port; + } + } + + if (!empty($challenge['domain'])) { + $prefixes = array(); + foreach (preg_split('/\\s+/', $challenge['domain']) as $prefix) { + // don't bother with different servers + if ('/' == substr($prefix, 0, 1)) { + $prefixes[] = $host . $prefix; + } + } + } + if (empty($prefixes)) { + $prefixes = array($host . '/'); + } + + $ret = true; + foreach ($prefixes as $prefix) { + if (!empty(self::$challenges[$prefix]) && + (empty($challenge['stale']) || strcasecmp('true', $challenge['stale'])) + ) { + // probably credentials are invalid + $ret = false; + } + self::$challenges[$prefix] =& $challenge; + } + return $ret; + } + + /** + * Checks whether another request should be performed with proxy digest auth + * + * Several conditions should be satisfied for it to return true: + * - response status should be 407 + * - proxy auth credentials should be set in the request object + * - response should contain Proxy-Authenticate header with digest challenge + * - there is either no challenge stored for this proxy or new challenge + * contains stale=true parameter (in other case we probably just failed + * due to invalid username / password) + * + * The method stores challenge values in $challenges static property + * + * @param HTTP_Request2_Response response to check + * @return boolean whether another request should be performed + * @throws HTTP_Request2_Exception in case of unsupported challenge parameters + */ + protected function shouldUseProxyDigestAuth(HTTP_Request2_Response $response) + { + if (407 != $response->getStatus() || !$this->request->getConfig('proxy_user')) { + return false; + } + if (!($challenge = $this->parseDigestChallenge($response->getHeader('proxy-authenticate')))) { + return false; + } + + $key = 'proxy://' . $this->request->getConfig('proxy_host') . + ':' . $this->request->getConfig('proxy_port'); + + if (!empty(self::$challenges[$key]) && + (empty($challenge['stale']) || strcasecmp('true', $challenge['stale'])) + ) { + $ret = false; + } else { + $ret = true; + } + self::$challenges[$key] = $challenge; + return $ret; + } + + /** + * Extracts digest method challenge from (WWW|Proxy)-Authenticate header value + * + * There is a problem with implementation of RFC 2617: several of the parameters + * are defined as quoted-string there and thus may contain backslash escaped + * double quotes (RFC 2616, section 2.2). However, RFC 2617 defines unq(X) as + * just value of quoted-string X without surrounding quotes, it doesn't speak + * about removing backslash escaping. + * + * Now realm parameter is user-defined and human-readable, strange things + * happen when it contains quotes: + * - Apache allows quotes in realm, but apparently uses realm value without + * backslashes for digest computation + * - Squid allows (manually escaped) quotes there, but it is impossible to + * authorize with either escaped or unescaped quotes used in digest, + * probably it can't parse the response (?) + * - Both IE and Firefox display realm value with backslashes in + * the password popup and apparently use the same value for digest + * + * HTTP_Request2 follows IE and Firefox (and hopefully RFC 2617) in + * quoted-string handling, unfortunately that means failure to authorize + * sometimes + * + * @param string value of WWW-Authenticate or Proxy-Authenticate header + * @return mixed associative array with challenge parameters, false if + * no challenge is present in header value + * @throws HTTP_Request2_Exception in case of unsupported challenge parameters + */ + protected function parseDigestChallenge($headerValue) + { + $authParam = '(' . self::REGEXP_TOKEN . ')\\s*=\\s*(' . + self::REGEXP_TOKEN . '|' . self::REGEXP_QUOTED_STRING . ')'; + $challenge = "!(?<=^|\\s|,)Digest ({$authParam}\\s*(,\\s*|$))+!"; + if (!preg_match($challenge, $headerValue, $matches)) { + return false; + } + + preg_match_all('!' . $authParam . '!', $matches[0], $params); + $paramsAry = array(); + $knownParams = array('realm', 'domain', 'nonce', 'opaque', 'stale', + 'algorithm', 'qop'); + for ($i = 0; $i < count($params[0]); $i++) { + // section 3.2.1: Any unrecognized directive MUST be ignored. + if (in_array($params[1][$i], $knownParams)) { + if ('"' == substr($params[2][$i], 0, 1)) { + $paramsAry[$params[1][$i]] = substr($params[2][$i], 1, -1); + } else { + $paramsAry[$params[1][$i]] = $params[2][$i]; + } + } + } + // we only support qop=auth + if (!empty($paramsAry['qop']) && + !in_array('auth', array_map('trim', explode(',', $paramsAry['qop']))) + ) { + throw new HTTP_Request2_Exception( + "Only 'auth' qop is currently supported in digest authentication, " . + "server requested '{$paramsAry['qop']}'" + ); + } + // we only support algorithm=MD5 + if (!empty($paramsAry['algorithm']) && 'MD5' != $paramsAry['algorithm']) { + throw new HTTP_Request2_Exception( + "Only 'MD5' algorithm is currently supported in digest authentication, " . + "server requested '{$paramsAry['algorithm']}'" + ); + } + + return $paramsAry; + } + + /** + * Parses [Proxy-]Authentication-Info header value and updates challenge + * + * @param array challenge to update + * @param string value of [Proxy-]Authentication-Info header + * @todo validate server rspauth response + */ + protected function updateChallenge(&$challenge, $headerValue) + { + $authParam = '!(' . self::REGEXP_TOKEN . ')\\s*=\\s*(' . + self::REGEXP_TOKEN . '|' . self::REGEXP_QUOTED_STRING . ')!'; + $paramsAry = array(); + + preg_match_all($authParam, $headerValue, $params); + for ($i = 0; $i < count($params[0]); $i++) { + if ('"' == substr($params[2][$i], 0, 1)) { + $paramsAry[$params[1][$i]] = substr($params[2][$i], 1, -1); + } else { + $paramsAry[$params[1][$i]] = $params[2][$i]; + } + } + // for now, just update the nonce value + if (!empty($paramsAry['nextnonce'])) { + $challenge['nonce'] = $paramsAry['nextnonce']; + $challenge['nc'] = 1; + } + } + + /** + * Creates a value for [Proxy-]Authorization header when using digest authentication + * + * @param string user name + * @param string password + * @param string request URL + * @param array digest challenge parameters + * @return string value of [Proxy-]Authorization request header + * @link http://tools.ietf.org/html/rfc2617#section-3.2.2 + */ + protected function createDigestResponse($user, $password, $url, &$challenge) + { + if (false !== ($q = strpos($url, '?')) && + $this->request->getConfig('digest_compat_ie') + ) { + $url = substr($url, 0, $q); + } + + $a1 = md5($user . ':' . $challenge['realm'] . ':' . $password); + $a2 = md5($this->request->getMethod() . ':' . $url); + + if (empty($challenge['qop'])) { + $digest = md5($a1 . ':' . $challenge['nonce'] . ':' . $a2); + } else { + $challenge['cnonce'] = 'Req2.' . rand(); + if (empty($challenge['nc'])) { + $challenge['nc'] = 1; + } + $nc = sprintf('%08x', $challenge['nc']++); + $digest = md5($a1 . ':' . $challenge['nonce'] . ':' . $nc . ':' . + $challenge['cnonce'] . ':auth:' . $a2); + } + return 'Digest username="' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $user) . '", ' . + 'realm="' . $challenge['realm'] . '", ' . + 'nonce="' . $challenge['nonce'] . '", ' . + 'uri="' . $url . '", ' . + 'response="' . $digest . '"' . + (!empty($challenge['opaque'])? + ', opaque="' . $challenge['opaque'] . '"': + '') . + (!empty($challenge['qop'])? + ', qop="auth", nc=' . $nc . ', cnonce="' . $challenge['cnonce'] . '"': + ''); + } + + /** + * Adds 'Authorization' header (if needed) to request headers array + * + * @param array request headers + * @param string request host (needed for digest authentication) + * @param string request URL (needed for digest authentication) + * @throws HTTP_Request2_Exception + */ + protected function addAuthorizationHeader(&$headers, $requestHost, $requestUrl) + { + if (!($auth = $this->request->getAuth())) { + return; + } + switch ($auth['scheme']) { + case HTTP_Request2::AUTH_BASIC: + $headers['authorization'] = + 'Basic ' . base64_encode($auth['user'] . ':' . $auth['password']); + break; + + case HTTP_Request2::AUTH_DIGEST: + unset($this->serverChallenge); + $fullUrl = ('/' == $requestUrl[0])? + $this->request->getUrl()->getScheme() . '://' . + $requestHost . $requestUrl: + $requestUrl; + foreach (array_keys(self::$challenges) as $key) { + if ($key == substr($fullUrl, 0, strlen($key))) { + $headers['authorization'] = $this->createDigestResponse( + $auth['user'], $auth['password'], + $requestUrl, self::$challenges[$key] + ); + $this->serverChallenge =& self::$challenges[$key]; + break; + } + } + break; + + default: + throw new HTTP_Request2_Exception( + "Unknown HTTP authentication scheme '{$auth['scheme']}'" + ); + } + } + + /** + * Adds 'Proxy-Authorization' header (if needed) to request headers array + * + * @param array request headers + * @param string request URL (needed for digest authentication) + * @throws HTTP_Request2_Exception + */ + protected function addProxyAuthorizationHeader(&$headers, $requestUrl) + { + if (!$this->request->getConfig('proxy_host') || + !($user = $this->request->getConfig('proxy_user')) || + (0 == strcasecmp('https', $this->request->getUrl()->getScheme()) && + HTTP_Request2::METHOD_CONNECT != $this->request->getMethod()) + ) { + return; + } + + $password = $this->request->getConfig('proxy_password'); + switch ($this->request->getConfig('proxy_auth_scheme')) { + case HTTP_Request2::AUTH_BASIC: + $headers['proxy-authorization'] = + 'Basic ' . base64_encode($user . ':' . $password); + break; + + case HTTP_Request2::AUTH_DIGEST: + unset($this->proxyChallenge); + $proxyUrl = 'proxy://' . $this->request->getConfig('proxy_host') . + ':' . $this->request->getConfig('proxy_port'); + if (!empty(self::$challenges[$proxyUrl])) { + $headers['proxy-authorization'] = $this->createDigestResponse( + $user, $password, + $requestUrl, self::$challenges[$proxyUrl] + ); + $this->proxyChallenge =& self::$challenges[$proxyUrl]; + } + break; + + default: + throw new HTTP_Request2_Exception( + "Unknown HTTP authentication scheme '" . + $this->request->getConfig('proxy_auth_scheme') . "'" + ); + } + } + + + /** + * Creates the string with the Request-Line and request headers + * + * @return string + * @throws HTTP_Request2_Exception + */ + protected function prepareHeaders() + { + $headers = $this->request->getHeaders(); + $url = $this->request->getUrl(); + $connect = HTTP_Request2::METHOD_CONNECT == $this->request->getMethod(); + $host = $url->getHost(); + + $defaultPort = 0 == strcasecmp($url->getScheme(), 'https')? 443: 80; + if (($port = $url->getPort()) && $port != $defaultPort || $connect) { + $host .= ':' . (empty($port)? $defaultPort: $port); + } + // Do not overwrite explicitly set 'Host' header, see bug #16146 + if (!isset($headers['host'])) { + $headers['host'] = $host; + } + + if ($connect) { + $requestUrl = $host; + + } else { + if (!$this->request->getConfig('proxy_host') || + 0 == strcasecmp($url->getScheme(), 'https') + ) { + $requestUrl = ''; + } else { + $requestUrl = $url->getScheme() . '://' . $host; + } + $path = $url->getPath(); + $query = $url->getQuery(); + $requestUrl .= (empty($path)? '/': $path) . (empty($query)? '': '?' . $query); + } + + if ('1.1' == $this->request->getConfig('protocol_version') && + extension_loaded('zlib') && !isset($headers['accept-encoding']) + ) { + $headers['accept-encoding'] = 'gzip, deflate'; + } + + $this->addAuthorizationHeader($headers, $host, $requestUrl); + $this->addProxyAuthorizationHeader($headers, $requestUrl); + $this->calculateRequestLength($headers); + + $headersStr = $this->request->getMethod() . ' ' . $requestUrl . ' HTTP/' . + $this->request->getConfig('protocol_version') . "\r\n"; + foreach ($headers as $name => $value) { + $canonicalName = implode('-', array_map('ucfirst', explode('-', $name))); + $headersStr .= $canonicalName . ': ' . $value . "\r\n"; + } + return $headersStr . "\r\n"; + } + + /** + * Sends the request body + * + * @throws HTTP_Request2_Exception + */ + protected function writeBody() + { + if (in_array($this->request->getMethod(), self::$bodyDisallowed) || + 0 == $this->contentLength + ) { + return; + } + + $position = 0; + $bufferSize = $this->request->getConfig('buffer_size'); + while ($position < $this->contentLength) { + if (is_string($this->requestBody)) { + $str = substr($this->requestBody, $position, $bufferSize); + } elseif (is_resource($this->requestBody)) { + $str = fread($this->requestBody, $bufferSize); + } else { + $str = $this->requestBody->read($bufferSize); + } + if (false === @fwrite($this->socket, $str, strlen($str))) { + throw new HTTP_Request2_Exception('Error writing request'); + } + // Provide the length of written string to the observer, request #7630 + $this->request->setLastEvent('sentBodyPart', strlen($str)); + $position += strlen($str); + } + } + + /** + * Reads the remote server's response + * + * @return HTTP_Request2_Response + * @throws HTTP_Request2_Exception + */ + protected function readResponse() + { + $bufferSize = $this->request->getConfig('buffer_size'); + + do { + $response = new HTTP_Request2_Response($this->readLine($bufferSize), true); + do { + $headerLine = $this->readLine($bufferSize); + $response->parseHeaderLine($headerLine); + } while ('' != $headerLine); + } while (in_array($response->getStatus(), array(100, 101))); + + $this->request->setLastEvent('receivedHeaders', $response); + + // No body possible in such responses + if (HTTP_Request2::METHOD_HEAD == $this->request->getMethod() || + (HTTP_Request2::METHOD_CONNECT == $this->request->getMethod() && + 200 <= $response->getStatus() && 300 > $response->getStatus()) || + in_array($response->getStatus(), array(204, 304)) + ) { + return $response; + } + + $chunked = 'chunked' == $response->getHeader('transfer-encoding'); + $length = $response->getHeader('content-length'); + $hasBody = false; + if ($chunked || null === $length || 0 < intval($length)) { + // RFC 2616, section 4.4: + // 3. ... If a message is received with both a + // Transfer-Encoding header field and a Content-Length header field, + // the latter MUST be ignored. + $toRead = ($chunked || null === $length)? null: $length; + $this->chunkLength = 0; + + while (!feof($this->socket) && (is_null($toRead) || 0 < $toRead)) { + if ($chunked) { + $data = $this->readChunked($bufferSize); + } elseif (is_null($toRead)) { + $data = $this->fread($bufferSize); + } else { + $data = $this->fread(min($toRead, $bufferSize)); + $toRead -= strlen($data); + } + if ('' == $data && (!$this->chunkLength || feof($this->socket))) { + break; + } + + $hasBody = true; + if ($this->request->getConfig('store_body')) { + $response->appendBody($data); + } + if (!in_array($response->getHeader('content-encoding'), array('identity', null))) { + $this->request->setLastEvent('receivedEncodedBodyPart', $data); + } else { + $this->request->setLastEvent('receivedBodyPart', $data); + } + } + } + + if ($hasBody) { + $this->request->setLastEvent('receivedBody', $response); + } + return $response; + } + + /** + * Reads until either the end of the socket or a newline, whichever comes first + * + * Strips the trailing newline from the returned data, handles global + * request timeout. Method idea borrowed from Net_Socket PEAR package. + * + * @param int buffer size to use for reading + * @return Available data up to the newline (not including newline) + * @throws HTTP_Request2_Exception In case of timeout + */ + protected function readLine($bufferSize) + { + $line = ''; + while (!feof($this->socket)) { + if ($this->deadline) { + stream_set_timeout($this->socket, max($this->deadline - time(), 1)); + } + $line .= @fgets($this->socket, $bufferSize); + $info = stream_get_meta_data($this->socket); + if ($info['timed_out'] || $this->deadline && time() > $this->deadline) { + $reason = $this->deadline + ? 'after ' . $this->request->getConfig('timeout') . ' second(s)' + : 'due to default_socket_timeout php.ini setting'; + throw new HTTP_Request2_Exception("Request timed out {$reason}"); + } + if (substr($line, -1) == "\n") { + return rtrim($line, "\r\n"); + } + } + return $line; + } + + /** + * Wrapper around fread(), handles global request timeout + * + * @param int Reads up to this number of bytes + * @return Data read from socket + * @throws HTTP_Request2_Exception In case of timeout + */ + protected function fread($length) + { + if ($this->deadline) { + stream_set_timeout($this->socket, max($this->deadline - time(), 1)); + } + $data = fread($this->socket, $length); + $info = stream_get_meta_data($this->socket); + if ($info['timed_out'] || $this->deadline && time() > $this->deadline) { + $reason = $this->deadline + ? 'after ' . $this->request->getConfig('timeout') . ' second(s)' + : 'due to default_socket_timeout php.ini setting'; + throw new HTTP_Request2_Exception("Request timed out {$reason}"); + } + return $data; + } + + /** + * Reads a part of response body encoded with chunked Transfer-Encoding + * + * @param int buffer size to use for reading + * @return string + * @throws HTTP_Request2_Exception + */ + protected function readChunked($bufferSize) + { + // at start of the next chunk? + if (0 == $this->chunkLength) { + $line = $this->readLine($bufferSize); + if (!preg_match('/^([0-9a-f]+)/i', $line, $matches)) { + throw new HTTP_Request2_Exception( + "Cannot decode chunked response, invalid chunk length '{$line}'" + ); + } else { + $this->chunkLength = hexdec($matches[1]); + // Chunk with zero length indicates the end + if (0 == $this->chunkLength) { + $this->readLine($bufferSize); + return ''; + } + } + } + $data = $this->fread(min($this->chunkLength, $bufferSize)); + $this->chunkLength -= strlen($data); + if (0 == $this->chunkLength) { + $this->readLine($bufferSize); // Trailing CRLF + } + return $data; + } +} + +?> \ No newline at end of file diff --git a/lib/php/HTTP/Request2/Exception.php b/lib/php/HTTP/Request2/Exception.php new file mode 100644 index 0000000000000000000000000000000000000000..d86a14401f97693fc82f2ce3750288af6a43382b --- /dev/null +++ b/lib/php/HTTP/Request2/Exception.php @@ -0,0 +1,62 @@ +<?php +/** + * Exception class for HTTP_Request2 package + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: Exception.php 290192 2009-11-03 21:29:32Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Base class for exceptions in PEAR + */ +require_once 'PEAR/Exception.php'; + +/** + * Exception class for HTTP_Request2 package + * + * Such a class is required by the Exception RFC: + * http://pear.php.net/pepr/pepr-proposal-show.php?id=132 + * + * @category HTTP + * @package HTTP_Request2 + * @version Release: 0.5.1 + */ +class HTTP_Request2_Exception extends PEAR_Exception +{ +} +?> \ No newline at end of file diff --git a/lib/php/HTTP/Request2/MultipartBody.php b/lib/php/HTTP/Request2/MultipartBody.php new file mode 100644 index 0000000000000000000000000000000000000000..efc2e254ab889025729e5089bce0555b92d3bb39 --- /dev/null +++ b/lib/php/HTTP/Request2/MultipartBody.php @@ -0,0 +1,274 @@ +<?php +/** + * Helper class for building multipart/form-data request body + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: MultipartBody.php 290192 2009-11-03 21:29:32Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Class for building multipart/form-data request body + * + * The class helps to reduce memory consumption by streaming large file uploads + * from disk, it also allows monitoring of upload progress (see request #7630) + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @version Release: 0.5.1 + * @link http://tools.ietf.org/html/rfc1867 + */ +class HTTP_Request2_MultipartBody +{ + /** + * MIME boundary + * @var string + */ + private $_boundary; + + /** + * Form parameters added via {@link HTTP_Request2::addPostParameter()} + * @var array + */ + private $_params = array(); + + /** + * File uploads added via {@link HTTP_Request2::addUpload()} + * @var array + */ + private $_uploads = array(); + + /** + * Header for parts with parameters + * @var string + */ + private $_headerParam = "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n"; + + /** + * Header for parts with uploads + * @var string + */ + private $_headerUpload = "--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\nContent-Type: %s\r\n\r\n"; + + /** + * Current position in parameter and upload arrays + * + * First number is index of "current" part, second number is position within + * "current" part + * + * @var array + */ + private $_pos = array(0, 0); + + + /** + * Constructor. Sets the arrays with POST data. + * + * @param array values of form fields set via {@link HTTP_Request2::addPostParameter()} + * @param array file uploads set via {@link HTTP_Request2::addUpload()} + * @param bool whether to append brackets to array variable names + */ + public function __construct(array $params, array $uploads, $useBrackets = true) + { + $this->_params = self::_flattenArray('', $params, $useBrackets); + foreach ($uploads as $fieldName => $f) { + if (!is_array($f['fp'])) { + $this->_uploads[] = $f + array('name' => $fieldName); + } else { + for ($i = 0; $i < count($f['fp']); $i++) { + $upload = array( + 'name' => ($useBrackets? $fieldName . '[' . $i . ']': $fieldName) + ); + foreach (array('fp', 'filename', 'size', 'type') as $key) { + $upload[$key] = $f[$key][$i]; + } + $this->_uploads[] = $upload; + } + } + } + } + + /** + * Returns the length of the body to use in Content-Length header + * + * @return integer + */ + public function getLength() + { + $boundaryLength = strlen($this->getBoundary()); + $headerParamLength = strlen($this->_headerParam) - 4 + $boundaryLength; + $headerUploadLength = strlen($this->_headerUpload) - 8 + $boundaryLength; + $length = $boundaryLength + 6; + foreach ($this->_params as $p) { + $length += $headerParamLength + strlen($p[0]) + strlen($p[1]) + 2; + } + foreach ($this->_uploads as $u) { + $length += $headerUploadLength + strlen($u['name']) + strlen($u['type']) + + strlen($u['filename']) + $u['size'] + 2; + } + return $length; + } + + /** + * Returns the boundary to use in Content-Type header + * + * @return string + */ + public function getBoundary() + { + if (empty($this->_boundary)) { + $this->_boundary = '--' . md5('PEAR-HTTP_Request2-' . microtime()); + } + return $this->_boundary; + } + + /** + * Returns next chunk of request body + * + * @param integer Amount of bytes to read + * @return string Up to $length bytes of data, empty string if at end + */ + public function read($length) + { + $ret = ''; + $boundary = $this->getBoundary(); + $paramCount = count($this->_params); + $uploadCount = count($this->_uploads); + while ($length > 0 && $this->_pos[0] <= $paramCount + $uploadCount) { + $oldLength = $length; + if ($this->_pos[0] < $paramCount) { + $param = sprintf($this->_headerParam, $boundary, + $this->_params[$this->_pos[0]][0]) . + $this->_params[$this->_pos[0]][1] . "\r\n"; + $ret .= substr($param, $this->_pos[1], $length); + $length -= min(strlen($param) - $this->_pos[1], $length); + + } elseif ($this->_pos[0] < $paramCount + $uploadCount) { + $pos = $this->_pos[0] - $paramCount; + $header = sprintf($this->_headerUpload, $boundary, + $this->_uploads[$pos]['name'], + $this->_uploads[$pos]['filename'], + $this->_uploads[$pos]['type']); + if ($this->_pos[1] < strlen($header)) { + $ret .= substr($header, $this->_pos[1], $length); + $length -= min(strlen($header) - $this->_pos[1], $length); + } + $filePos = max(0, $this->_pos[1] - strlen($header)); + if ($length > 0 && $filePos < $this->_uploads[$pos]['size']) { + $ret .= fread($this->_uploads[$pos]['fp'], $length); + $length -= min($length, $this->_uploads[$pos]['size'] - $filePos); + } + if ($length > 0) { + $start = $this->_pos[1] + ($oldLength - $length) - + strlen($header) - $this->_uploads[$pos]['size']; + $ret .= substr("\r\n", $start, $length); + $length -= min(2 - $start, $length); + } + + } else { + $closing = '--' . $boundary . "--\r\n"; + $ret .= substr($closing, $this->_pos[1], $length); + $length -= min(strlen($closing) - $this->_pos[1], $length); + } + if ($length > 0) { + $this->_pos = array($this->_pos[0] + 1, 0); + } else { + $this->_pos[1] += $oldLength; + } + } + return $ret; + } + + /** + * Sets the current position to the start of the body + * + * This allows reusing the same body in another request + */ + public function rewind() + { + $this->_pos = array(0, 0); + foreach ($this->_uploads as $u) { + rewind($u['fp']); + } + } + + /** + * Returns the body as string + * + * Note that it reads all file uploads into memory so it is a good idea not + * to use this method with large file uploads and rely on read() instead. + * + * @return string + */ + public function __toString() + { + $this->rewind(); + return $this->read($this->getLength()); + } + + + /** + * Helper function to change the (probably multidimensional) associative array + * into the simple one. + * + * @param string name for item + * @param mixed item's values + * @param bool whether to append [] to array variables' names + * @return array array with the following items: array('item name', 'item value'); + */ + private static function _flattenArray($name, $values, $useBrackets) + { + if (!is_array($values)) { + return array(array($name, $values)); + } else { + $ret = array(); + foreach ($values as $k => $v) { + if (empty($name)) { + $newName = $k; + } elseif ($useBrackets) { + $newName = $name . '[' . $k . ']'; + } else { + $newName = $name; + } + $ret = array_merge($ret, self::_flattenArray($newName, $v, $useBrackets)); + } + return $ret; + } + } +} +?> diff --git a/lib/php/HTTP/Request2/Observer/Log.php b/lib/php/HTTP/Request2/Observer/Log.php new file mode 100644 index 0000000000000000000000000000000000000000..e7a064b9603cb040a98342e56e6cd6efe56896c0 --- /dev/null +++ b/lib/php/HTTP/Request2/Observer/Log.php @@ -0,0 +1,215 @@ +<?php +/** + * An observer useful for debugging / testing. + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author David Jean Louis <izi@php.net> + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: Log.php 290743 2009-11-14 13:27:49Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Exception class for HTTP_Request2 package + */ +require_once 'HTTP/Request2/Exception.php'; + +/** + * A debug observer useful for debugging / testing. + * + * This observer logs to a log target data corresponding to the various request + * and response events, it logs by default to php://output but can be configured + * to log to a file or via the PEAR Log package. + * + * A simple example: + * <code> + * require_once 'HTTP/Request2.php'; + * require_once 'HTTP/Request2/Observer/Log.php'; + * + * $request = new HTTP_Request2('http://www.example.com'); + * $observer = new HTTP_Request2_Observer_Log(); + * $request->attach($observer); + * $request->send(); + * </code> + * + * A more complex example with PEAR Log: + * <code> + * require_once 'HTTP/Request2.php'; + * require_once 'HTTP/Request2/Observer/Log.php'; + * require_once 'Log.php'; + * + * $request = new HTTP_Request2('http://www.example.com'); + * // we want to log with PEAR log + * $observer = new HTTP_Request2_Observer_Log(Log::factory('console')); + * + * // we only want to log received headers + * $observer->events = array('receivedHeaders'); + * + * $request->attach($observer); + * $request->send(); + * </code> + * + * @category HTTP + * @package HTTP_Request2 + * @author David Jean Louis <izi@php.net> + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 0.5.1 + * @link http://pear.php.net/package/HTTP_Request2 + */ +class HTTP_Request2_Observer_Log implements SplObserver +{ + // properties {{{ + + /** + * The log target, it can be a a resource or a PEAR Log instance. + * + * @var resource|Log $target + */ + protected $target = null; + + /** + * The events to log. + * + * @var array $events + */ + public $events = array( + 'connect', + 'sentHeaders', + 'sentBodyPart', + 'receivedHeaders', + 'receivedBody', + 'disconnect', + ); + + // }}} + // __construct() {{{ + + /** + * Constructor. + * + * @param mixed $target Can be a file path (default: php://output), a resource, + * or an instance of the PEAR Log class. + * @param array $events Array of events to listen to (default: all events) + * + * @return void + */ + public function __construct($target = 'php://output', array $events = array()) + { + if (!empty($events)) { + $this->events = $events; + } + if (is_resource($target) || $target instanceof Log) { + $this->target = $target; + } elseif (false === ($this->target = @fopen($target, 'w'))) { + throw new HTTP_Request2_Exception("Unable to open '{$target}'"); + } + } + + // }}} + // update() {{{ + + /** + * Called when the request notifies us of an event. + * + * @param HTTP_Request2 $subject The HTTP_Request2 instance + * + * @return void + */ + public function update(SplSubject $subject) + { + $event = $subject->getLastEvent(); + if (!in_array($event['name'], $this->events)) { + return; + } + + switch ($event['name']) { + case 'connect': + $this->log('* Connected to ' . $event['data']); + break; + case 'sentHeaders': + $headers = explode("\r\n", $event['data']); + array_pop($headers); + foreach ($headers as $header) { + $this->log('> ' . $header); + } + break; + case 'sentBodyPart': + $this->log('> ' . $event['data'] . ' byte(s) sent'); + break; + case 'receivedHeaders': + $this->log(sprintf('< HTTP/%s %s %s', + $event['data']->getVersion(), + $event['data']->getStatus(), + $event['data']->getReasonPhrase())); + $headers = $event['data']->getHeader(); + foreach ($headers as $key => $val) { + $this->log('< ' . $key . ': ' . $val); + } + $this->log('< '); + break; + case 'receivedBody': + $this->log($event['data']->getBody()); + break; + case 'disconnect': + $this->log('* Disconnected'); + break; + } + } + + // }}} + // log() {{{ + + /** + * Logs the given message to the configured target. + * + * @param string $message Message to display + * + * @return void + */ + protected function log($message) + { + if ($this->target instanceof Log) { + $this->target->debug($message); + } elseif (is_resource($this->target)) { + fwrite($this->target, $message . "\r\n"); + } + } + + // }}} +} + +?> \ No newline at end of file diff --git a/lib/php/HTTP/Request2/Response.php b/lib/php/HTTP/Request2/Response.php new file mode 100644 index 0000000000000000000000000000000000000000..217fbdf6b913e3fa29465539e86763b8a4d85a62 --- /dev/null +++ b/lib/php/HTTP/Request2/Response.php @@ -0,0 +1,559 @@ +<?php +/** + * Class representing a HTTP response + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: Response.php 290520 2009-11-11 20:09:42Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Exception class for HTTP_Request2 package + */ +require_once 'HTTP/Request2/Exception.php'; + +/** + * Class representing a HTTP response + * + * The class is designed to be used in "streaming" scenario, building the + * response as it is being received: + * <code> + * $statusLine = read_status_line(); + * $response = new HTTP_Request2_Response($statusLine); + * do { + * $headerLine = read_header_line(); + * $response->parseHeaderLine($headerLine); + * } while ($headerLine != ''); + * + * while ($chunk = read_body()) { + * $response->appendBody($chunk); + * } + * + * var_dump($response->getHeader(), $response->getCookies(), $response->getBody()); + * </code> + * + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @version Release: 0.5.1 + * @link http://tools.ietf.org/html/rfc2616#section-6 + */ +class HTTP_Request2_Response +{ + /** + * HTTP protocol version (e.g. 1.0, 1.1) + * @var string + */ + protected $version; + + /** + * Status code + * @var integer + * @link http://tools.ietf.org/html/rfc2616#section-6.1.1 + */ + protected $code; + + /** + * Reason phrase + * @var string + * @link http://tools.ietf.org/html/rfc2616#section-6.1.1 + */ + protected $reasonPhrase; + + /** + * Associative array of response headers + * @var array + */ + protected $headers = array(); + + /** + * Cookies set in the response + * @var array + */ + protected $cookies = array(); + + /** + * Name of last header processed by parseHederLine() + * + * Used to handle the headers that span multiple lines + * + * @var string + */ + protected $lastHeader = null; + + /** + * Response body + * @var string + */ + protected $body = ''; + + /** + * Whether the body is still encoded by Content-Encoding + * + * cURL provides the decoded body to the callback; if we are reading from + * socket the body is still gzipped / deflated + * + * @var bool + */ + protected $bodyEncoded; + + /** + * Associative array of HTTP status code / reason phrase. + * + * @var array + * @link http://tools.ietf.org/html/rfc2616#section-10 + */ + protected static $phrases = array( + + // 1xx: Informational - Request received, continuing process + 100 => 'Continue', + 101 => 'Switching Protocols', + + // 2xx: Success - The action was successfully received, understood and + // accepted + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + + // 3xx: Redirection - Further action must be taken in order to complete + // the request + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', // 1.1 + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + + // 4xx: Client Error - The request contains bad syntax or cannot be + // fulfilled + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + + // 5xx: Server Error - The server failed to fulfill an apparently + // valid request + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 509 => 'Bandwidth Limit Exceeded', + + ); + + /** + * Constructor, parses the response status line + * + * @param string Response status line (e.g. "HTTP/1.1 200 OK") + * @param bool Whether body is still encoded by Content-Encoding + * @throws HTTP_Request2_Exception if status line is invalid according to spec + */ + public function __construct($statusLine, $bodyEncoded = true) + { + if (!preg_match('!^HTTP/(\d\.\d) (\d{3})(?: (.+))?!', $statusLine, $m)) { + throw new HTTP_Request2_Exception("Malformed response: {$statusLine}"); + } + $this->version = $m[1]; + $this->code = intval($m[2]); + if (!empty($m[3])) { + $this->reasonPhrase = trim($m[3]); + } elseif (!empty(self::$phrases[$this->code])) { + $this->reasonPhrase = self::$phrases[$this->code]; + } + $this->bodyEncoded = (bool)$bodyEncoded; + } + + /** + * Parses the line from HTTP response filling $headers array + * + * The method should be called after reading the line from socket or receiving + * it into cURL callback. Passing an empty string here indicates the end of + * response headers and triggers additional processing, so be sure to pass an + * empty string in the end. + * + * @param string Line from HTTP response + */ + public function parseHeaderLine($headerLine) + { + $headerLine = trim($headerLine, "\r\n"); + + // empty string signals the end of headers, process the received ones + if ('' == $headerLine) { + if (!empty($this->headers['set-cookie'])) { + $cookies = is_array($this->headers['set-cookie'])? + $this->headers['set-cookie']: + array($this->headers['set-cookie']); + foreach ($cookies as $cookieString) { + $this->parseCookie($cookieString); + } + unset($this->headers['set-cookie']); + } + foreach (array_keys($this->headers) as $k) { + if (is_array($this->headers[$k])) { + $this->headers[$k] = implode(', ', $this->headers[$k]); + } + } + + // string of the form header-name: header value + } elseif (preg_match('!^([^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+):(.+)$!', $headerLine, $m)) { + $name = strtolower($m[1]); + $value = trim($m[2]); + if (empty($this->headers[$name])) { + $this->headers[$name] = $value; + } else { + if (!is_array($this->headers[$name])) { + $this->headers[$name] = array($this->headers[$name]); + } + $this->headers[$name][] = $value; + } + $this->lastHeader = $name; + + // continuation of a previous header + } elseif (preg_match('!^\s+(.+)$!', $headerLine, $m) && $this->lastHeader) { + if (!is_array($this->headers[$this->lastHeader])) { + $this->headers[$this->lastHeader] .= ' ' . trim($m[1]); + } else { + $key = count($this->headers[$this->lastHeader]) - 1; + $this->headers[$this->lastHeader][$key] .= ' ' . trim($m[1]); + } + } + } + + /** + * Parses a Set-Cookie header to fill $cookies array + * + * @param string value of Set-Cookie header + * @link http://web.archive.org/web/20080331104521/http://cgi.netscape.com/newsref/std/cookie_spec.html + */ + protected function parseCookie($cookieString) + { + $cookie = array( + 'expires' => null, + 'domain' => null, + 'path' => null, + 'secure' => false + ); + + // Only a name=value pair + if (!strpos($cookieString, ';')) { + $pos = strpos($cookieString, '='); + $cookie['name'] = trim(substr($cookieString, 0, $pos)); + $cookie['value'] = trim(substr($cookieString, $pos + 1)); + + // Some optional parameters are supplied + } else { + $elements = explode(';', $cookieString); + $pos = strpos($elements[0], '='); + $cookie['name'] = trim(substr($elements[0], 0, $pos)); + $cookie['value'] = trim(substr($elements[0], $pos + 1)); + + for ($i = 1; $i < count($elements); $i++) { + if (false === strpos($elements[$i], '=')) { + $elName = trim($elements[$i]); + $elValue = null; + } else { + list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i])); + } + $elName = strtolower($elName); + if ('secure' == $elName) { + $cookie['secure'] = true; + } elseif ('expires' == $elName) { + $cookie['expires'] = str_replace('"', '', $elValue); + } elseif ('path' == $elName || 'domain' == $elName) { + $cookie[$elName] = urldecode($elValue); + } else { + $cookie[$elName] = $elValue; + } + } + } + $this->cookies[] = $cookie; + } + + /** + * Appends a string to the response body + * @param string + */ + public function appendBody($bodyChunk) + { + $this->body .= $bodyChunk; + } + + /** + * Returns the status code + * @return integer + */ + public function getStatus() + { + return $this->code; + } + + /** + * Returns the reason phrase + * @return string + */ + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + /** + * Whether response is a redirect that can be automatically handled by HTTP_Request2 + * @return bool + */ + public function isRedirect() + { + return in_array($this->code, array(300, 301, 302, 303, 307)) + && isset($this->headers['location']); + } + + /** + * Returns either the named header or all response headers + * + * @param string Name of header to return + * @return string|array Value of $headerName header (null if header is + * not present), array of all response headers if + * $headerName is null + */ + public function getHeader($headerName = null) + { + if (null === $headerName) { + return $this->headers; + } else { + $headerName = strtolower($headerName); + return isset($this->headers[$headerName])? $this->headers[$headerName]: null; + } + } + + /** + * Returns cookies set in response + * + * @return array + */ + public function getCookies() + { + return $this->cookies; + } + + /** + * Returns the body of the response + * + * @return string + * @throws HTTP_Request2_Exception if body cannot be decoded + */ + public function getBody() + { + if (!$this->bodyEncoded || + !in_array(strtolower($this->getHeader('content-encoding')), array('gzip', 'deflate')) + ) { + return $this->body; + + } else { + if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) { + $oldEncoding = mb_internal_encoding(); + mb_internal_encoding('iso-8859-1'); + } + + try { + switch (strtolower($this->getHeader('content-encoding'))) { + case 'gzip': + $decoded = self::decodeGzip($this->body); + break; + case 'deflate': + $decoded = self::decodeDeflate($this->body); + } + } catch (Exception $e) { + } + + if (!empty($oldEncoding)) { + mb_internal_encoding($oldEncoding); + } + if (!empty($e)) { + throw $e; + } + return $decoded; + } + } + + /** + * Get the HTTP version of the response + * + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * Decodes the message-body encoded by gzip + * + * The real decoding work is done by gzinflate() built-in function, this + * method only parses the header and checks data for compliance with + * RFC 1952 + * + * @param string gzip-encoded data + * @return string decoded data + * @throws HTTP_Request2_Exception + * @link http://tools.ietf.org/html/rfc1952 + */ + public static function decodeGzip($data) + { + $length = strlen($data); + // If it doesn't look like gzip-encoded data, don't bother + if (18 > $length || strcmp(substr($data, 0, 2), "\x1f\x8b")) { + return $data; + } + if (!function_exists('gzinflate')) { + throw new HTTP_Request2_Exception('Unable to decode body: gzip extension not available'); + } + $method = ord(substr($data, 2, 1)); + if (8 != $method) { + throw new HTTP_Request2_Exception('Error parsing gzip header: unknown compression method'); + } + $flags = ord(substr($data, 3, 1)); + if ($flags & 224) { + throw new HTTP_Request2_Exception('Error parsing gzip header: reserved bits are set'); + } + + // header is 10 bytes minimum. may be longer, though. + $headerLength = 10; + // extra fields, need to skip 'em + if ($flags & 4) { + if ($length - $headerLength - 2 < 8) { + throw new HTTP_Request2_Exception('Error parsing gzip header: data too short'); + } + $extraLength = unpack('v', substr($data, 10, 2)); + if ($length - $headerLength - 2 - $extraLength[1] < 8) { + throw new HTTP_Request2_Exception('Error parsing gzip header: data too short'); + } + $headerLength += $extraLength[1] + 2; + } + // file name, need to skip that + if ($flags & 8) { + if ($length - $headerLength - 1 < 8) { + throw new HTTP_Request2_Exception('Error parsing gzip header: data too short'); + } + $filenameLength = strpos(substr($data, $headerLength), chr(0)); + if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) { + throw new HTTP_Request2_Exception('Error parsing gzip header: data too short'); + } + $headerLength += $filenameLength + 1; + } + // comment, need to skip that also + if ($flags & 16) { + if ($length - $headerLength - 1 < 8) { + throw new HTTP_Request2_Exception('Error parsing gzip header: data too short'); + } + $commentLength = strpos(substr($data, $headerLength), chr(0)); + if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) { + throw new HTTP_Request2_Exception('Error parsing gzip header: data too short'); + } + $headerLength += $commentLength + 1; + } + // have a CRC for header. let's check + if ($flags & 2) { + if ($length - $headerLength - 2 < 8) { + throw new HTTP_Request2_Exception('Error parsing gzip header: data too short'); + } + $crcReal = 0xffff & crc32(substr($data, 0, $headerLength)); + $crcStored = unpack('v', substr($data, $headerLength, 2)); + if ($crcReal != $crcStored[1]) { + throw new HTTP_Request2_Exception('Header CRC check failed'); + } + $headerLength += 2; + } + // unpacked data CRC and size at the end of encoded data + $tmp = unpack('V2', substr($data, -8)); + $dataCrc = $tmp[1]; + $dataSize = $tmp[2]; + + // finally, call the gzinflate() function + // don't pass $dataSize to gzinflate, see bugs #13135, #14370 + $unpacked = gzinflate(substr($data, $headerLength, -8)); + if (false === $unpacked) { + throw new HTTP_Request2_Exception('gzinflate() call failed'); + } elseif ($dataSize != strlen($unpacked)) { + throw new HTTP_Request2_Exception('Data size check failed'); + } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) { + throw new HTTP_Request2_Exception('Data CRC check failed'); + } + return $unpacked; + } + + /** + * Decodes the message-body encoded by deflate + * + * @param string deflate-encoded data + * @return string decoded data + * @throws HTTP_Request2_Exception + */ + public static function decodeDeflate($data) + { + if (!function_exists('gzuncompress')) { + throw new HTTP_Request2_Exception('Unable to decode body: gzip extension not available'); + } + // RFC 2616 defines 'deflate' encoding as zlib format from RFC 1950, + // while many applications send raw deflate stream from RFC 1951. + // We should check for presence of zlib header and use gzuncompress() or + // gzinflate() as needed. See bug #15305 + $header = unpack('n', substr($data, 0, 2)); + return (0 == $header[1] % 31)? gzuncompress($data): gzinflate($data); + } +} +?> \ No newline at end of file diff --git a/lib/php/Net/URL2.php b/lib/php/Net/URL2.php new file mode 100644 index 0000000000000000000000000000000000000000..08cc7b11cb84e927aba0a96f67795e8789cce6e2 --- /dev/null +++ b/lib/php/Net/URL2.php @@ -0,0 +1,913 @@ +<?php +/** + * Net_URL2, a class representing a URL as per RFC 3986. + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2007-2009, Peytz & Co. A/S + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * * Neither the name of the Net_URL2 nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_URL2 + * @author Christian Schmidt <schmidt@php.net> + * @copyright 2007-2009 Peytz & Co. A/S + * @license http://www.opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: URL2.php 289017 2009-09-30 20:16:39Z schmidt $ + * @link http://www.rfc-editor.org/rfc/rfc3986.txt + */ + +/** + * Represents a URL as per RFC 3986. + * + * @category Networking + * @package Net_URL2 + * @author Christian Schmidt <schmidt@php.net> + * @copyright 2007-2009 Peytz & Co. A/S + * @license http://www.opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/Net_URL2 + */ +class Net_URL2 +{ + /** + * Do strict parsing in resolve() (see RFC 3986, section 5.2.2). Default + * is true. + */ + const OPTION_STRICT = 'strict'; + + /** + * Represent arrays in query using PHP's [] notation. Default is true. + */ + const OPTION_USE_BRACKETS = 'use_brackets'; + + /** + * URL-encode query variable keys. Default is true. + */ + const OPTION_ENCODE_KEYS = 'encode_keys'; + + /** + * Query variable separators when parsing the query string. Every character + * is considered a separator. Default is "&". + */ + const OPTION_SEPARATOR_INPUT = 'input_separator'; + + /** + * Query variable separator used when generating the query string. Default + * is "&". + */ + const OPTION_SEPARATOR_OUTPUT = 'output_separator'; + + /** + * Default options corresponds to how PHP handles $_GET. + */ + private $_options = array( + self::OPTION_STRICT => true, + self::OPTION_USE_BRACKETS => true, + self::OPTION_ENCODE_KEYS => true, + self::OPTION_SEPARATOR_INPUT => '&', + self::OPTION_SEPARATOR_OUTPUT => '&', + ); + + /** + * @var string|bool + */ + private $_scheme = false; + + /** + * @var string|bool + */ + private $_userinfo = false; + + /** + * @var string|bool + */ + private $_host = false; + + /** + * @var string|bool + */ + private $_port = false; + + /** + * @var string + */ + private $_path = ''; + + /** + * @var string|bool + */ + private $_query = false; + + /** + * @var string|bool + */ + private $_fragment = false; + + /** + * Constructor. + * + * @param string $url an absolute or relative URL + * @param array $options an array of OPTION_xxx constants + */ + public function __construct($url, $options = null) + { + if (is_array($options)) { + foreach ($options as $optionName => $value) { + $this->setOption($optionName, $value); + } + } + + // The regular expression is copied verbatim from RFC 3986, appendix B. + // The expression does not validate the URL but matches any string. + preg_match('!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!', + $url, + $matches); + + // "path" is always present (possibly as an empty string); the rest + // are optional. + $this->_scheme = !empty($matches[1]) ? $matches[2] : false; + $this->setAuthority(!empty($matches[3]) ? $matches[4] : false); + $this->_path = $matches[5]; + $this->_query = !empty($matches[6]) ? $matches[7] : false; + $this->_fragment = !empty($matches[8]) ? $matches[9] : false; + } + + /** + * Magic Setter. + * + * This method will magically set the value of a private variable ($var) + * with the value passed as the args + * + * @param string $var The private variable to set. + * @param mixed $arg An argument of any type. + * @return void + */ + public function __set($var, $arg) + { + $method = 'set' . $var; + if (method_exists($this, $method)) { + $this->$method($arg); + } + } + + /** + * Magic Getter. + * + * This is the magic get method to retrieve the private variable + * that was set by either __set() or it's setter... + * + * @param string $var The property name to retrieve. + * @return mixed $this->$var Either a boolean false if the + * property is not set or the value + * of the private property. + */ + public function __get($var) + { + $method = 'get' . $var; + if (method_exists($this, $method)) { + return $this->$method(); + } + + return false; + } + + /** + * Returns the scheme, e.g. "http" or "urn", or false if there is no + * scheme specified, i.e. if this is a relative URL. + * + * @return string|bool + */ + public function getScheme() + { + return $this->_scheme; + } + + /** + * Sets the scheme, e.g. "http" or "urn". Specify false if there is no + * scheme specified, i.e. if this is a relative URL. + * + * @param string|bool $scheme e.g. "http" or "urn", or false if there is no + * scheme specified, i.e. if this is a relative + * URL + * + * @return void + * @see getScheme() + */ + public function setScheme($scheme) + { + $this->_scheme = $scheme; + } + + /** + * Returns the user part of the userinfo part (the part preceding the first + * ":"), or false if there is no userinfo part. + * + * @return string|bool + */ + public function getUser() + { + return $this->_userinfo !== false + ? preg_replace('@:.*$@', '', $this->_userinfo) + : false; + } + + /** + * Returns the password part of the userinfo part (the part after the first + * ":"), or false if there is no userinfo part (i.e. the URL does not + * contain "@" in front of the hostname) or the userinfo part does not + * contain ":". + * + * @return string|bool + */ + public function getPassword() + { + return $this->_userinfo !== false + ? substr(strstr($this->_userinfo, ':'), 1) + : false; + } + + /** + * Returns the userinfo part, or false if there is none, i.e. if the + * authority part does not contain "@". + * + * @return string|bool + */ + public function getUserinfo() + { + return $this->_userinfo; + } + + /** + * Sets the userinfo part. If two arguments are passed, they are combined + * in the userinfo part as username ":" password. + * + * @param string|bool $userinfo userinfo or username + * @param string|bool $password optional password, or false + * + * @return void + */ + public function setUserinfo($userinfo, $password = false) + { + $this->_userinfo = $userinfo; + if ($password !== false) { + $this->_userinfo .= ':' . $password; + } + } + + /** + * Returns the host part, or false if there is no authority part, e.g. + * relative URLs. + * + * @return string|bool a hostname, an IP address, or false + */ + public function getHost() + { + return $this->_host; + } + + /** + * Sets the host part. Specify false if there is no authority part, e.g. + * relative URLs. + * + * @param string|bool $host a hostname, an IP address, or false + * + * @return void + */ + public function setHost($host) + { + $this->_host = $host; + } + + /** + * Returns the port number, or false if there is no port number specified, + * i.e. if the default port is to be used. + * + * @return string|bool + */ + public function getPort() + { + return $this->_port; + } + + /** + * Sets the port number. Specify false if there is no port number specified, + * i.e. if the default port is to be used. + * + * @param string|bool $port a port number, or false + * + * @return void + */ + public function setPort($port) + { + $this->_port = $port; + } + + /** + * Returns the authority part, i.e. [ userinfo "@" ] host [ ":" port ], or + * false if there is no authority. + * + * @return string|bool + */ + public function getAuthority() + { + if (!$this->_host) { + return false; + } + + $authority = ''; + + if ($this->_userinfo !== false) { + $authority .= $this->_userinfo . '@'; + } + + $authority .= $this->_host; + + if ($this->_port !== false) { + $authority .= ':' . $this->_port; + } + + return $authority; + } + + /** + * Sets the authority part, i.e. [ userinfo "@" ] host [ ":" port ]. Specify + * false if there is no authority. + * + * @param string|false $authority a hostname or an IP addresse, possibly + * with userinfo prefixed and port number + * appended, e.g. "foo:bar@example.org:81". + * + * @return void + */ + public function setAuthority($authority) + { + $this->_userinfo = false; + $this->_host = false; + $this->_port = false; + if (preg_match('@^(([^\@]*)\@)?([^:]+)(:(\d*))?$@', $authority, $reg)) { + if ($reg[1]) { + $this->_userinfo = $reg[2]; + } + + $this->_host = $reg[3]; + if (isset($reg[5])) { + $this->_port = $reg[5]; + } + } + } + + /** + * Returns the path part (possibly an empty string). + * + * @return string + */ + public function getPath() + { + return $this->_path; + } + + /** + * Sets the path part (possibly an empty string). + * + * @param string $path a path + * + * @return void + */ + public function setPath($path) + { + $this->_path = $path; + } + + /** + * Returns the query string (excluding the leading "?"), or false if "?" + * is not present in the URL. + * + * @return string|bool + * @see self::getQueryVariables() + */ + public function getQuery() + { + return $this->_query; + } + + /** + * Sets the query string (excluding the leading "?"). Specify false if "?" + * is not present in the URL. + * + * @param string|bool $query a query string, e.g. "foo=1&bar=2" + * + * @return void + * @see self::setQueryVariables() + */ + public function setQuery($query) + { + $this->_query = $query; + } + + /** + * Returns the fragment name, or false if "#" is not present in the URL. + * + * @return string|bool + */ + public function getFragment() + { + return $this->_fragment; + } + + /** + * Sets the fragment name. Specify false if "#" is not present in the URL. + * + * @param string|bool $fragment a fragment excluding the leading "#", or + * false + * + * @return void + */ + public function setFragment($fragment) + { + $this->_fragment = $fragment; + } + + /** + * Returns the query string like an array as the variables would appear in + * $_GET in a PHP script. If the URL does not contain a "?", an empty array + * is returned. + * + * @return array + */ + public function getQueryVariables() + { + $pattern = '/[' . + preg_quote($this->getOption(self::OPTION_SEPARATOR_INPUT), '/') . + ']/'; + $parts = preg_split($pattern, $this->_query, -1, PREG_SPLIT_NO_EMPTY); + $return = array(); + + foreach ($parts as $part) { + if (strpos($part, '=') !== false) { + list($key, $value) = explode('=', $part, 2); + } else { + $key = $part; + $value = null; + } + + if ($this->getOption(self::OPTION_ENCODE_KEYS)) { + $key = rawurldecode($key); + } + $value = rawurldecode($value); + + if ($this->getOption(self::OPTION_USE_BRACKETS) && + preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $key, $matches)) { + + $key = $matches[1]; + $idx = $matches[2]; + + // Ensure is an array + if (empty($return[$key]) || !is_array($return[$key])) { + $return[$key] = array(); + } + + // Add data + if ($idx === '') { + $return[$key][] = $value; + } else { + $return[$key][$idx] = $value; + } + } elseif (!$this->getOption(self::OPTION_USE_BRACKETS) + && !empty($return[$key]) + ) { + $return[$key] = (array) $return[$key]; + $return[$key][] = $value; + } else { + $return[$key] = $value; + } + } + + return $return; + } + + /** + * Sets the query string to the specified variable in the query string. + * + * @param array $array (name => value) array + * + * @return void + */ + public function setQueryVariables(array $array) + { + if (!$array) { + $this->_query = false; + } else { + foreach ($array as $name => $value) { + if ($this->getOption(self::OPTION_ENCODE_KEYS)) { + $name = self::urlencode($name); + } + + if (is_array($value)) { + foreach ($value as $k => $v) { + $parts[] = $this->getOption(self::OPTION_USE_BRACKETS) + ? sprintf('%s[%s]=%s', $name, $k, $v) + : ($name . '=' . $v); + } + } elseif (!is_null($value)) { + $parts[] = $name . '=' . self::urlencode($value); + } else { + $parts[] = $name; + } + } + $this->_query = implode($this->getOption(self::OPTION_SEPARATOR_OUTPUT), + $parts); + } + } + + /** + * Sets the specified variable in the query string. + * + * @param string $name variable name + * @param mixed $value variable value + * + * @return array + */ + public function setQueryVariable($name, $value) + { + $array = $this->getQueryVariables(); + $array[$name] = $value; + $this->setQueryVariables($array); + } + + /** + * Removes the specifed variable from the query string. + * + * @param string $name a query string variable, e.g. "foo" in "?foo=1" + * + * @return void + */ + public function unsetQueryVariable($name) + { + $array = $this->getQueryVariables(); + unset($array[$name]); + $this->setQueryVariables($array); + } + + /** + * Returns a string representation of this URL. + * + * @return string + */ + public function getURL() + { + // See RFC 3986, section 5.3 + $url = ""; + + if ($this->_scheme !== false) { + $url .= $this->_scheme . ':'; + } + + $authority = $this->getAuthority(); + if ($authority !== false) { + $url .= '//' . $authority; + } + $url .= $this->_path; + + if ($this->_query !== false) { + $url .= '?' . $this->_query; + } + + if ($this->_fragment !== false) { + $url .= '#' . $this->_fragment; + } + + return $url; + } + + /** + * Returns a string representation of this URL. + * + * @return string + * @see toString() + */ + public function __toString() + { + return $this->getURL(); + } + + /** + * Returns a normalized string representation of this URL. This is useful + * for comparison of URLs. + * + * @return string + */ + public function getNormalizedURL() + { + $url = clone $this; + $url->normalize(); + return $url->getUrl(); + } + + /** + * Returns a normalized Net_URL2 instance. + * + * @return Net_URL2 + */ + public function normalize() + { + // See RFC 3886, section 6 + + // Schemes are case-insensitive + if ($this->_scheme) { + $this->_scheme = strtolower($this->_scheme); + } + + // Hostnames are case-insensitive + if ($this->_host) { + $this->_host = strtolower($this->_host); + } + + // Remove default port number for known schemes (RFC 3986, section 6.2.3) + if ($this->_port && + $this->_scheme && + $this->_port == getservbyname($this->_scheme, 'tcp')) { + + $this->_port = false; + } + + // Normalize case of %XX percentage-encodings (RFC 3986, section 6.2.2.1) + foreach (array('_userinfo', '_host', '_path') as $part) { + if ($this->$part) { + $this->$part = preg_replace('/%[0-9a-f]{2}/ie', + 'strtoupper("\0")', + $this->$part); + } + } + + // Path segment normalization (RFC 3986, section 6.2.2.3) + $this->_path = self::removeDotSegments($this->_path); + + // Scheme based normalization (RFC 3986, section 6.2.3) + if ($this->_host && !$this->_path) { + $this->_path = '/'; + } + } + + /** + * Returns whether this instance represents an absolute URL. + * + * @return bool + */ + public function isAbsolute() + { + return (bool) $this->_scheme; + } + + /** + * Returns an Net_URL2 instance representing an absolute URL relative to + * this URL. + * + * @param Net_URL2|string $reference relative URL + * + * @return Net_URL2 + */ + public function resolve($reference) + { + if (!$reference instanceof Net_URL2) { + $reference = new self($reference); + } + if (!$this->isAbsolute()) { + throw new Exception('Base-URL must be absolute'); + } + + // A non-strict parser may ignore a scheme in the reference if it is + // identical to the base URI's scheme. + if (!$this->getOption(self::OPTION_STRICT) && $reference->_scheme == $this->_scheme) { + $reference->_scheme = false; + } + + $target = new self(''); + if ($reference->_scheme !== false) { + $target->_scheme = $reference->_scheme; + $target->setAuthority($reference->getAuthority()); + $target->_path = self::removeDotSegments($reference->_path); + $target->_query = $reference->_query; + } else { + $authority = $reference->getAuthority(); + if ($authority !== false) { + $target->setAuthority($authority); + $target->_path = self::removeDotSegments($reference->_path); + $target->_query = $reference->_query; + } else { + if ($reference->_path == '') { + $target->_path = $this->_path; + if ($reference->_query !== false) { + $target->_query = $reference->_query; + } else { + $target->_query = $this->_query; + } + } else { + if (substr($reference->_path, 0, 1) == '/') { + $target->_path = self::removeDotSegments($reference->_path); + } else { + // Merge paths (RFC 3986, section 5.2.3) + if ($this->_host !== false && $this->_path == '') { + $target->_path = '/' . $this->_path; + } else { + $i = strrpos($this->_path, '/'); + if ($i !== false) { + $target->_path = substr($this->_path, 0, $i + 1); + } + $target->_path .= $reference->_path; + } + $target->_path = self::removeDotSegments($target->_path); + } + $target->_query = $reference->_query; + } + $target->setAuthority($this->getAuthority()); + } + $target->_scheme = $this->_scheme; + } + + $target->_fragment = $reference->_fragment; + + return $target; + } + + /** + * Removes dots as described in RFC 3986, section 5.2.4, e.g. + * "/foo/../bar/baz" => "/bar/baz" + * + * @param string $path a path + * + * @return string a path + */ + public static function removeDotSegments($path) + { + $output = ''; + + // Make sure not to be trapped in an infinite loop due to a bug in this + // method + $j = 0; + while ($path && $j++ < 100) { + if (substr($path, 0, 2) == './') { + // Step 2.A + $path = substr($path, 2); + } elseif (substr($path, 0, 3) == '../') { + // Step 2.A + $path = substr($path, 3); + } elseif (substr($path, 0, 3) == '/./' || $path == '/.') { + // Step 2.B + $path = '/' . substr($path, 3); + } elseif (substr($path, 0, 4) == '/../' || $path == '/..') { + // Step 2.C + $path = '/' . substr($path, 4); + $i = strrpos($output, '/'); + $output = $i === false ? '' : substr($output, 0, $i); + } elseif ($path == '.' || $path == '..') { + // Step 2.D + $path = ''; + } else { + // Step 2.E + $i = strpos($path, '/'); + if ($i === 0) { + $i = strpos($path, '/', 1); + } + if ($i === false) { + $i = strlen($path); + } + $output .= substr($path, 0, $i); + $path = substr($path, $i); + } + } + + return $output; + } + + /** + * Percent-encodes all non-alphanumeric characters except these: _ . - ~ + * Similar to PHP's rawurlencode(), except that it also encodes ~ in PHP + * 5.2.x and earlier. + * + * @param $raw the string to encode + * @return string + */ + public static function urlencode($string) + { + $encoded = rawurlencode($string); + // This is only necessary in PHP < 5.3. + $encoded = str_replace('%7E', '~', $encoded); + return $encoded; + } + + /** + * Returns a Net_URL2 instance representing the canonical URL of the + * currently executing PHP script. + * + * @return string + */ + public static function getCanonical() + { + if (!isset($_SERVER['REQUEST_METHOD'])) { + // ALERT - no current URL + throw new Exception('Script was not called through a webserver'); + } + + // Begin with a relative URL + $url = new self($_SERVER['PHP_SELF']); + $url->_scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http'; + $url->_host = $_SERVER['SERVER_NAME']; + $port = $_SERVER['SERVER_PORT']; + if ($url->_scheme == 'http' && $port != 80 || + $url->_scheme == 'https' && $port != 443) { + + $url->_port = $port; + } + return $url; + } + + /** + * Returns the URL used to retrieve the current request. + * + * @return string + */ + public static function getRequestedURL() + { + return self::getRequested()->getUrl(); + } + + /** + * Returns a Net_URL2 instance representing the URL used to retrieve the + * current request. + * + * @return Net_URL2 + */ + public static function getRequested() + { + if (!isset($_SERVER['REQUEST_METHOD'])) { + // ALERT - no current URL + throw new Exception('Script was not called through a webserver'); + } + + // Begin with a relative URL + $url = new self($_SERVER['REQUEST_URI']); + $url->_scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http'; + // Set host and possibly port + $url->setAuthority($_SERVER['HTTP_HOST']); + return $url; + } + + /** + * Sets the specified option. + * + * @param string $optionName a self::OPTION_ constant + * @param mixed $value option value + * + * @return void + * @see self::OPTION_STRICT + * @see self::OPTION_USE_BRACKETS + * @see self::OPTION_ENCODE_KEYS + */ + function setOption($optionName, $value) + { + if (!array_key_exists($optionName, $this->_options)) { + return false; + } + $this->_options[$optionName] = $value; + } + + /** + * Returns the value of the specified option. + * + * @param string $optionName The name of the option to retrieve + * + * @return mixed + */ + function getOption($optionName) + { + return isset($this->_options[$optionName]) + ? $this->_options[$optionName] : false; + } +} diff --git a/lib/php/Services/W3C/HTMLValidator.php b/lib/php/Services/W3C/HTMLValidator.php new file mode 100644 index 0000000000000000000000000000000000000000..c47291ea45787f4bcb76d4fde2894228d5f42ace --- /dev/null +++ b/lib/php/Services/W3C/HTMLValidator.php @@ -0,0 +1,407 @@ +<?php +/** + * This file contains the base class for utilizing an instance of the + * W3 HTML Validator. + * + * PHP versions 5 + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @version CVS: $id$ + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ + +/** + * require the HTTP_Request2 class for sending requests to the validator. + */ +require_once 'HTTP/Request2.php'; + +/** + * Uses response object. + */ +require_once 'Services/W3C/HTMLValidator/Response.php'; + +require_once 'Services/W3C/HTMLValidator/Error.php'; + +require_once 'Services/W3C/HTMLValidator/Warning.php'; + +/** + * A simple class for utilizing the W3C HTML Validator service. + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + */ +class Services_W3C_HTMLValidator +{ + /** + * URI to the w3 validator. + * + * @var string + */ + public $validator_uri = 'http://validator.w3.org/check'; + + /** + * The URL of the document to validate + * @var string + */ + public $uri; + + /** + * Internally used filename of a file to upload to the validator + * POSTed as multipart/form-data + * @var string + */ + public $uploaded_file; + + /** + * HTML fragment to validate. + * + * Full documents only. At the moment, will only work if data is sent with the + * UTF-8 encoding. + * @var string + */ + public $fragment; + + /** + * Output format + * + * Triggers the various outputs formats of the validator. If unset, the usual + * Web format will be sent. If set to soap12, the SOAP1.2 interface will be + * triggered. See below for the SOAP 1.2 response format description. + */ + public $output = 'soap12'; + + /** + * Character encoding + * + * Character encoding override: Specify the character encoding to use when + * parsing the document. When used with the auxiliary parameter fbc set to 1, + * the given encoding will only be used as a fallback value, in case the charset + * is absent or unrecognized. Note that this parameter is ignored if validating + * a fragment with the direct input interface. + * @var string + */ + public $charset; + + /** + * Fall Back Character Set + * + * When no character encoding is detected in the document, use the value in + * $charset as the fallback character set. + * + * @var bool + */ + public $fbc; + + /** + * Document type + * + * Document Type override: Specify the Document Type (DOCTYPE) to use when + * parsing the document. When used with the auxiliary parameter fbd set to 1, + * the given document type will only be used as a fallback value, in case the + * document's DOCTYPE declaration is missing or unrecognized. + * @var string + */ + public $doctype; + + /** + * Fall back doctype + * + * When set to 1, use the value stored in $doctype when the document type + * cannot be automatically determined. + * + * @var bool + */ + public $fbd; + + /** + * Verbose output + * + * In the web interface, when set to 1, will make error messages, explanations + * and other diagnostics more verbose. + * In SOAP output, does not have any impact. + * @var bool + */ + public $verbose = false; + + /** + * Show source + * + * In the web interface, triggers the display of the source after the validation + * results. In SOAP output, does not have any impact. + * @var bool + */ + public $ss = false; + + /** + * outline + * + * In the web interface, when set to 1, triggers the display of the document + * outline after the validation results. In SOAP output, does not have any + * impact. + * @var bool + */ + public $outline = false; + + /** + * HTTP_Request2 object. + * @var object + */ + protected $request; + + /** + * Constructor for the class. + * + * @param array $options An array of options + */ + function __construct($options = array()) + { + $this->setOptions($options); + + $this->setRequest(new HTTP_Request2()); + } + + /** + * Sets options for the class. + * Pass an associative array of options for the class. + * + * @param array $options array of options + * + * @return void + */ + function setOptions($options) + { + foreach ($options as $option=>$val) { + $this->$option = $val; + } + } + + /** + * Sets the HTTP request object to use. + * + * @param HTTP_Request2 $request The request object. + * + * @return Services_W3C_HTMLValidator + */ + function setRequest(HTTP_Request2 $request) + { + $this->request = $request; + + return $this; + } + + /** + * Validates a given URI + * + * Executes the validator using the current parameters and returns a Response + * object on success. + * + * @param string $uri The address to the page to validate ex: http://example.com/ + * + * @return mixed object Services_W3C_HTMLValidator | bool false + */ + function validate($uri) + { + $this->uri = $uri; + $this->buildRequest('uri'); + if ($response = $this->sendRequest()) { + return $this->parseSOAP12Response($response->getBody()); + } else { + return false; + } + } + + /** + * Validates the local file + * + * Requests validation on the local file, from an instance of the W3C validator. + * The file is posted to the W3 validator using multipart/form-data. + * + * @param string $file file to be validated. + * + * @return mixed object Services_W3C_HTMLValidator | bool fals + */ + function validateFile($file) + { + if (file_exists($file)) { + $this->uploaded_file = $file; + $this->buildRequest('file'); + if ($response = $this->sendRequest()) { + return $this->parseSOAP12Response($response->getBody()); + } else { + return false; + } + } else { + // No such file! + return false; + } + } + + /** + * Validate an html string + * + * @param string $html full html document fragment + * + * @return mixed object Services_W3C_HTMLValidator | bool fals + */ + function validateFragment($html) + { + $this->fragment = $html; + $this->buildRequest('fragment'); + if ($response = $this->sendRequest()) { + return $this->parseSOAP12Response($response->getBody()); + } else { + return false; + } + } + + /** + * Prepares a request object to send to the validator. + * + * @param string $type uri, file, or fragment + * + * @return void + */ + protected function buildRequest($type = 'uri') + { + $this->request->setURL($this->validator_uri); + switch ($type) { + case 'uri': + default: + $this->request->setMethod(HTTP_Request2::METHOD_GET); + $this->setQueryVariable('uri', $this->uri); + $method = 'setQueryVariable'; + break; + case 'file': + $this->request->setMethod(HTTP_Request2::METHOD_POST); + $this->request->addUpload('uploaded_file', + $this->uploaded_file, + null, + 'text/html'); + $method = 'addPostParameter'; + break; + case 'fragment': + $this->request->setMethod(HTTP_Request2::METHOD_POST); + $this->addPostParameter('fragment', $this->fragment); + $method = 'addPostParameter'; + break; + } + + foreach (array( 'charset', + 'fbc', + 'doctype', + 'fbd', + 'verbose', + 'ss', + 'outline', + 'output') as $option) { + if (isset($this->$option)) { + if (is_bool($this->$option)) { + $this->$method($option, intval($this->$option)); + } else { + $this->$method($option, $this->$option); + } + } + } + } + + /** + * Set a querystring variable for the request + * + * @param string $name Name of the querystring parameter + * @param mixed $value Value of the parameter + * + * @return void + */ + protected function setQueryVariable($name, $value = '') + { + $url =& $this->request->getURL(); + $url->setQueryVariable($name, $value); + $this->request->setURL($url); + } + + /** + * Add post data to the request + * + * @param string $name Name of the post field + * @param mixed $value Value of the field + * + * @return void + */ + protected function addPostParameter($name, $value = '') + { + $this->request->addPostParameter($name, $value); + } + + /** + * Actually sends the request to the HTML Validator service + * + * @return bool true | false + */ + protected function sendRequest() + { + try { + return $this->request->send(); + } catch (Exception $e) { + throw new Exception('Error sending request', null, $e); + return false; + } + + } + + /** + * Parse an XML response from the validator + * + * This function parses a SOAP 1.2 response xml string from the validator. + * + * @param string $xml The raw soap12 XML response from the validator. + * + * @return mixed object Services_W3C_HTMLValidator_Response | bool false + */ + static function parseSOAP12Response($xml) + { + $doc = new DOMDocument(); + if ($doc->loadXML($xml)) { + $response = new Services_W3C_HTMLValidator_Response(); + + // Get the standard CDATA elements + foreach (array('uri','checkedby','doctype','charset') as $var) { + $element = $doc->getElementsByTagName($var); + if ($element->length) { + $response->$var = $element->item(0)->nodeValue; + } + } + // Handle the bool element validity + $element = $doc->getElementsByTagName('validity'); + if ($element->length && + $element->item(0)->nodeValue == 'true') { + $response->validity = true; + } else { + $response->validity = false; + } + if (!$response->validity) { + $errors = $doc->getElementsByTagName('error'); + foreach ($errors as $error) { + $response->errors[] = + new Services_W3C_HTMLValidator_Error($error); + } + } + $warnings = $doc->getElementsByTagName('warning'); + foreach ($warnings as $warning) { + $response->warnings[] = + new Services_W3C_HTMLValidator_Warning($warning); + } + return $response; + } else { + // Could not load the XML. + return false; + } + } +} +?> \ No newline at end of file diff --git a/lib/php/Services/W3C/HTMLValidator/Error.php b/lib/php/Services/W3C/HTMLValidator/Error.php new file mode 100644 index 0000000000000000000000000000000000000000..4a222cebe1ed64eff0ae10b404074347ebc0ed82 --- /dev/null +++ b/lib/php/Services/W3C/HTMLValidator/Error.php @@ -0,0 +1,35 @@ +<?php +/** + * This file contains a simple class for error messages returned from the validator. + * + * PHP versions 5 + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @version CVS: $id$ + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ + +/** + * Extends from the Message class + */ +require_once 'Services/W3C/HTMLValidator/Message.php'; + +/** + * The message class holds an error response from the W3 validator. + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + */ +class Services_W3C_HTMLValidator_Error extends Services_W3C_HTMLValidator_Message +{ + +} + +?> \ No newline at end of file diff --git a/lib/php/Services/W3C/HTMLValidator/Message.php b/lib/php/Services/W3C/HTMLValidator/Message.php new file mode 100644 index 0000000000000000000000000000000000000000..4274bc6138d03f4f3792bafc043c92cc49249182 --- /dev/null +++ b/lib/php/Services/W3C/HTMLValidator/Message.php @@ -0,0 +1,97 @@ +<?php +/** + * This file provides a base class for messages from a W3C validator. + * + * PHP versions 5 + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @version CVS: $id$ + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ + +/** + * The message class holds a response from the W3 validator. + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + */ +class Services_W3C_HTMLValidator_Message +{ + /** + * line corresponding to the message + * + * Within the source code of the validated document, refers to the line which + * caused this message. + * @var int + */ + public $line; + + /** + * column corresponding to the message + * + * Within the source code of the validated document, refers to the column within + * the line for the message. + * @var int + */ + public $col; + + /** + * The actual message + * @var string + */ + public $message; + + /** + * Unique ID for this message + * + * not implemented yet. should be the number of the error, as addressed + * internally by the validator + * @var int + */ + public $messageid; + + /** + * Explanation for this message. + * + * HTML snippet which describes the message, usually with information on + * how to correct the problem. + * @var string + */ + public $explanation; + + /** + * Source which caused the message. + * + * the snippet of HTML code which invoked the message to give the + * context of the e + * @var string + */ + public $source; + + /** + * Constructor for a response message + * + * @param object $node A dom document node. + */ + function __construct($node = null) + { + if (isset($node)) { + foreach (get_class_vars('Services_W3C_HTMLValidator_Message') as + $var => $val) { + $element = $node->getElementsByTagName($var); + if ($element->length) { + $this->$var = $element->item(0)->nodeValue; + } + } + } + } +} + +?> \ No newline at end of file diff --git a/lib/php/Services/W3C/HTMLValidator/Response.php b/lib/php/Services/W3C/HTMLValidator/Response.php new file mode 100644 index 0000000000000000000000000000000000000000..9184790be6289056cf683a432ea7a436e127a798 --- /dev/null +++ b/lib/php/Services/W3C/HTMLValidator/Response.php @@ -0,0 +1,92 @@ +<?php +/** + * File contains a simple class structure for holding a response from the + * W3C HTMLValidator software. + * + * PHP versions 5 + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @version CVS: $id$ + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ + +/** + * Simple class for a W3C HTML Validator Response. + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + */ +class Services_W3C_HTMLValidator_Response +{ + /** + * the address of the document validated + * + * Will (likely?) be upload://Form Submission + * if an uploaded document or fragment was validated. In EARL terms, this is + * the TestSubject. + * @var string + */ + public $uri; + + /** + * Location of the service which provided the validation result. In EARL terms, + * this is the Assertor. + * @var string + */ + public $checkedby; + + /** + * Detected (or forced) Document Type for the validated document + * @var string + */ + public $doctype; + + /** + * Detected (or forced) Character Encoding for the validated document + * @var string + */ + public $charset; + + /** + * Whether or not the document validated passed or not formal validation + * (true|false boolean) + * @var bool + */ + public $validity; + + /** + * Array of HTMLValidator_Error objects (if applicable) + * @var array + */ + public $errors = array(); + + /** + * Array of HTMLValidator_Warning objects (if applicable) + * @var array + */ + public $warnings = array(); + + /** + * Returns the validity of the checked document. + * + * @return bool + */ + function isValid() + { + if ($this->validity) { + return true; + } else { + return false; + } + } + +} + +?> \ No newline at end of file diff --git a/lib/php/Services/W3C/HTMLValidator/Warning.php b/lib/php/Services/W3C/HTMLValidator/Warning.php new file mode 100644 index 0000000000000000000000000000000000000000..2032ef9124801e36324d3f163ef2dce2b383ff08 --- /dev/null +++ b/lib/php/Services/W3C/HTMLValidator/Warning.php @@ -0,0 +1,35 @@ +<?php +/** + * This file contains a simple class for warning messages returned from a validator. + * + * PHP versions 5 + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @version CVS: $id$ + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ + +/** + * Extends from the Message class + */ +require_once 'Services/W3C/HTMLValidator/Message.php'; + +/** + * The message class holds an error response from the W3 validator. + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + */ +class Services_W3C_HTMLValidator_Warning extends Services_W3C_HTMLValidator_Message +{ + +} + +?> \ No newline at end of file diff --git a/lib/php/Spider.php b/lib/php/Spider.php new file mode 100644 index 0000000000000000000000000000000000000000..aacc1817b4ebc961562883f318aa336e0fea7360 --- /dev/null +++ b/lib/php/Spider.php @@ -0,0 +1,167 @@ +<?php +/** + * pear2\Spider\Main + * + * PHP version 5 + * + * @category Yourcategory + * @package PEAR2_Spider + * @author Your Name <handle@php.net> + * @copyright 2010 Your Name + * @license http://www.opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id$ + * @link http://svn.php.net/repository/pear2/PEAR2_Spider + */ + +/** + * Main class for PEAR2_Spider + * + * @category Yourcategory + * @package PEAR2_Spider + * @author Your Name <handle@php.net> + * @copyright 2010 Your Name + * @license http://www.opensource.org/licenses/bsd-license.php New BSD License + * @link http://svn.php.net/repository/pear2/PEAR2_Spider + */ +class Spider +{ + const MAX_DEPTH = 50; + + protected $loggers = array(); + protected $filters = array(); + protected $downloader = null; + protected $parser = null; + protected $visited = array(); + + public function __construct( + Spider_Downloader $downloader, + Spider_Parser $parser) + { + $this->setDownloader($downloader); + $this->setParser($parser); + } + + public function setDownloader(Spider_Downloader $downloader) + { + $this->downloader = $downloader; + } + + public function setParser(Spider_ParserInterface $parser) + { + $this->parser = $parser; + } + + public function addLogger(Spider_LoggerAbstract $logger) + { + if (!in_array($logger, $this->loggers)) { + $this->loggers[] = $logger; + } + } + + public function addUriFilter($filterClass) + { + if (!in_array($filterClass, $this->filters)) { + $this->filters[] = $filterClass; + } + } + + public function spider($baseUri) + { + if (!filter_var($baseUri, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED)) { + throw new Exception('Invalid URI: ' . $baseUri); + } + $this->spiderPage($baseUri, $baseUri); + } + + protected function spiderPage($baseUri, $uri, $depth = 1) + { + + $this->visited[$uri] = true; + + $content = $this->downloader->download($uri); + $xpath = $this->parser->parse($content, $uri); + + foreach ($this->loggers as $logger) { + $logger->log($uri, $xpath); + } + + // spider sub-pages + if ($depth < self::MAX_DEPTH) { + $subUris = $this->getUris($baseUri, $xpath); + + foreach ($this->filters as $filter_class) { + $subUris = new $filter_class($subUris); + } + + foreach ($subUris as $subUri) { + if (!array_key_exists($subUri, $this->visited)) { + try { + $this->spiderPage($baseUri, $subUri, $depth + 1); + } catch(Exception $e) { + throw new Exception($baseUri . ' linked to a page that does not exist!' .$subUri, 404, $e); + } + } + } + } + } + + protected function getUris($baseUri, DOMXPath $xpath) + { + $uris = array(); + + $baseHrefNodes = $xpath->query( + "//xhtml:base/@href" + ); + + if ($baseHrefNodes->length > 0) { + $baseHref = (string)$baseHrefNodes->item(0)->nodeValue; + } else { + $baseHref = ''; + } + + $nodes = $xpath->query( + "//xhtml:a[@href]/@href" + ); + + foreach ($nodes as $node) { + $uri = $this->absolutePath((string)$node->nodeValue, $baseUri); + + if (!empty($uri)) { + if (strncmp($baseUri, $uri, strlen($baseUri)) === 0) { + $uris[] = $uri; + } elseif ( + $uri != '.' + && preg_match('!^(https?|ftp)://!i', $uri) === 0 + ) { + $uris[] = $baseHref . $uri; + } + } + } + + return new Spider_UriIterator($uris); + } + + public function absolutePath($relativeUri, $baseUri) + { + $new_base_url = $baseUri; + $base_url_parts = parse_url($baseUri); + + if (substr($baseUri, -1) != '/') { + $path = pathinfo($base_url_parts['path']); + $new_base_url = substr($new_base_url, 0, strlen($new_base_url)-strlen($path['basename'])); + } + + $new_txt = ''; + + if (!filter_var($relativeUri, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED)) { + if (substr($relativeUri, 0, 1) == '/') { + $new_base_url = $base_url_parts['scheme'].'://'.$base_url_parts['host']; + } + $new_txt .= $new_base_url; + } + + $absoluteUri = $new_txt.$relativeUri; + + return $absoluteUri; + } +} diff --git a/lib/php/Spider/AnchorFilter.php b/lib/php/Spider/AnchorFilter.php new file mode 100644 index 0000000000000000000000000000000000000000..684a4bad4f38417957923bb88f021c134330c938 --- /dev/null +++ b/lib/php/Spider/AnchorFilter.php @@ -0,0 +1,13 @@ +<?php +class Spider_AnchorFilter extends Spider_UriFilterInterface +{ + function accept() + { + return true; + } + + function current() + { + return preg_replace('/#(.*)/', '', parent::current()); + } +} \ No newline at end of file diff --git a/lib/php/Spider/DbLogger.php b/lib/php/Spider/DbLogger.php new file mode 100644 index 0000000000000000000000000000000000000000..7a2c571de62b473a808da2852987a9d1914bcec4 --- /dev/null +++ b/lib/php/Spider/DbLogger.php @@ -0,0 +1,10 @@ +<?php +abstract class Spider_DbLogger extends Spider_LoggerAbstract +{ + protected $db = null; + + public function __construct(PDO $db) + { + $this->db = $db; + } +} \ No newline at end of file diff --git a/lib/php/Spider/Downloader.php b/lib/php/Spider/Downloader.php new file mode 100644 index 0000000000000000000000000000000000000000..118471cb0ccfbc585da31a26aae5a1412f86cc2c --- /dev/null +++ b/lib/php/Spider/Downloader.php @@ -0,0 +1,36 @@ +<?php +class Spider_Downloader +{ + private $curl = null; + + public function __construct() + { + $this->curl = curl_init(); + + curl_setopt_array( + $this->curl, + array( + CURLOPT_AUTOREFERER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_USERAGENT => 'silverorange-spider', + ) + ); + } + + public function download($uri) + { + curl_setopt($this->curl, CURLOPT_URL, $uri); + $result = curl_exec($this->curl); + if (!$result) { + throw new Exception('Error downloading ' . $uri. $result); + } + return $result; + } + + public function __destruct() + { + curl_close($this->curl); + } +} diff --git a/lib/php/Spider/JavaScriptLogger.php b/lib/php/Spider/JavaScriptLogger.php new file mode 100644 index 0000000000000000000000000000000000000000..f3df2a4c1b22063f4601ab3b9f8a0182f2cd6b99 --- /dev/null +++ b/lib/php/Spider/JavaScriptLogger.php @@ -0,0 +1,36 @@ +<?php +class Spider_JavaScriptLogger extends Spider_DbLogger +{ + public function log($uri, DOMXPath $xpath) + { + $scripts = $this->getScripts($xpath); + foreach ($scripts as $script) { + $this->saveScript($uri, $script); + } + } + + protected function getScripts(DOMXPath $xpath) + { + $scripts = array(); + + $nodes = $xpath->query( + "//xhtml:script[@type='text/javascript' and @src]/@src" + ); + + foreach ($nodes as $node) { + $scripts[] = (string)$node->nodeValue; + } + + return $scripts; + } + + protected function saveScript($uri, $script) + { + $statement = $this->db->prepare( + 'insert into SpiderJavaScript (uri, script) ' . + 'values (:uri, :script)' + ); + + $statement->execute(array($uri, $script)); + } +} \ No newline at end of file diff --git a/lib/php/Spider/Logger.php b/lib/php/Spider/Logger.php new file mode 100644 index 0000000000000000000000000000000000000000..5f698baaee43935d33b272104d947b14091189f7 --- /dev/null +++ b/lib/php/Spider/Logger.php @@ -0,0 +1,8 @@ +<?php +class Spider_Logger extends Spider_LoggerAbstract +{ + public function log($uri, DOMXPath $xpath) + { + echo $uri . PHP_EOL; + } +} \ No newline at end of file diff --git a/lib/php/Spider/LoggerAbstract.php b/lib/php/Spider/LoggerAbstract.php new file mode 100644 index 0000000000000000000000000000000000000000..73748abeca5c6332867531365529015ffec33207 --- /dev/null +++ b/lib/php/Spider/LoggerAbstract.php @@ -0,0 +1,5 @@ +<?php +abstract class Spider_LoggerAbstract +{ + abstract public function log($uri, DOMXPath $xpath); +} \ No newline at end of file diff --git a/lib/php/Spider/MailtoFilter.php b/lib/php/Spider/MailtoFilter.php new file mode 100644 index 0000000000000000000000000000000000000000..c7486579929651a680c8f6b0082d55648b2790e6 --- /dev/null +++ b/lib/php/Spider/MailtoFilter.php @@ -0,0 +1,11 @@ +<?php +class Spider_MailtoFilter extends Spider_UriFilterInterface +{ + function accept() + { + if (substr($this->current(), 0, 7) == 'mailto:') { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/lib/php/Spider/PageLogger.php b/lib/php/Spider/PageLogger.php new file mode 100644 index 0000000000000000000000000000000000000000..4c2ba4c5250998ffe90aaae8eee8fae95e5cceb4 --- /dev/null +++ b/lib/php/Spider/PageLogger.php @@ -0,0 +1,18 @@ +<?php +class Spider_PageLogger extends Spider_DbLogger +{ + public function log($uri, DOMXPath $xpath) + { + $this->savePage($uri); + } + + protected function savePage($uri) + { + $statement = $this->db->prepare( + 'insert into SpiderPage (uri) ' . + 'values (:uri)' + ); + + $statement->execute(array($uri)); + } +} diff --git a/lib/php/Spider/Parser.php b/lib/php/Spider/Parser.php new file mode 100644 index 0000000000000000000000000000000000000000..7c839d3629faf8cb67b2bc237a0da442d284c8d8 --- /dev/null +++ b/lib/php/Spider/Parser.php @@ -0,0 +1,21 @@ +<?php +class Spider_Parser implements Spider_ParserInterface +{ + public function parse($content) + { + return $this->getXPath($content); + } + + protected function getXPath($content) + { + $document = new DOMDocument(); + $document->strictErrorChecking = false; + $document->loadXML( + $content, + LIBXML_NOERROR | LIBXML_NONET | LIBXML_NOWARNING + ); + $xpath = new DOMXPAth($document); + $xpath->registerNamespace('xhtml', 'http://www.w3.org/1999/xhtml'); + return $xpath; + } +} \ No newline at end of file diff --git a/lib/php/Spider/ParserInterface.php b/lib/php/Spider/ParserInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..417e895b27a08932bd3ca2df161ebb1f6a1f6004 --- /dev/null +++ b/lib/php/Spider/ParserInterface.php @@ -0,0 +1,13 @@ +<?php +interface Spider_ParserInterface +{ + /** + * parse the content and return xpath object + * + * @param string $content The content + * + * @return DOMXPAth + */ + public function parse($content); + +} \ No newline at end of file diff --git a/lib/php/Spider/StyleSheetLogger.php b/lib/php/Spider/StyleSheetLogger.php new file mode 100644 index 0000000000000000000000000000000000000000..8eab27df78fafa2acba62477cf7bfefce5bd319d --- /dev/null +++ b/lib/php/Spider/StyleSheetLogger.php @@ -0,0 +1,37 @@ +<?php +class Spider_StyleSheetLogger extends Spider_DbLogger +{ + public function log($uri, DOMXPath $xpath) + { + $styles = $this->getStyles($xpath); + foreach ($styles as $style) { + $this->saveStyle($uri, $style); + } + } + + protected function getStyles(DOMXPath $xpath) + { + $styles = array(); + + $nodes = $xpath->query( + "//xhtml:link[@rel='stylesheet' and @type='text/css' and @href]/" . + "@href" + ); + + foreach ($nodes as $node) { + $styles[] = (string)$node->nodeValue; + } + + return $styles; + } + + protected function saveStyle($uri, $style) + { + $statement = $this->db->prepare( + 'insert into SpiderStyleSheet (uri, style) ' . + 'values (:uri, :style)' + ); + + $statement->execute(array($uri, $style)); + } +} \ No newline at end of file diff --git a/lib/php/Spider/TidyParser.php b/lib/php/Spider/TidyParser.php new file mode 100644 index 0000000000000000000000000000000000000000..0cd8b8b0560979fdc831257e2c39535cefddccba --- /dev/null +++ b/lib/php/Spider/TidyParser.php @@ -0,0 +1,21 @@ +<?php +class Spider_TidyParser extends Spider_Parser +{ + public function parse($content) + { + $content = $this->tidy($content); + return $this->getXPath($content); + } + + protected function tidy($content) + { + $config = array( + 'output-xhtml' => true, + ); + + $tidy = tidy_parse_string($content, $config, 'utf8'); + $tidy->cleanRepair(); + + return (string)$tidy; + } +} \ No newline at end of file diff --git a/lib/php/Spider/UriFilterInterface.php b/lib/php/Spider/UriFilterInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..c502442a033f2a0873ddd04988547e2902505a20 --- /dev/null +++ b/lib/php/Spider/UriFilterInterface.php @@ -0,0 +1,5 @@ +<?php +abstract class Spider_UriFilterInterface extends FilterIterator +{ + +} \ No newline at end of file diff --git a/lib/php/Spider/UriIterator.php b/lib/php/Spider/UriIterator.php new file mode 100644 index 0000000000000000000000000000000000000000..04bccaaacddd5e92d6985096a5bd9014a0887961 --- /dev/null +++ b/lib/php/Spider/UriIterator.php @@ -0,0 +1,5 @@ +<?php +class Spider_UriIterator extends ArrayIterator +{ + +} \ No newline at end of file diff --git a/lib/tests/HTTP_Request2/HTTP/tests/AllTests.php b/lib/tests/HTTP_Request2/HTTP/tests/AllTests.php new file mode 100644 index 0000000000000000000000000000000000000000..7e84beadd4a85bb4eb27e6f29dd2bbdd1e5c98ca --- /dev/null +++ b/lib/tests/HTTP_Request2/HTTP/tests/AllTests.php @@ -0,0 +1,79 @@ +<?php +/** + * Unit tests for HTTP_Request2 package + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: AllTests.php 290192 2009-11-03 21:29:32Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +if (!defined('PHPUnit_MAIN_METHOD')) { + define('PHPUnit_MAIN_METHOD', 'HTTP_Request2_AllTests::main'); +} + +require_once 'PHPUnit/Framework.php'; +require_once 'PHPUnit/TextUI/TestRunner.php'; + +chdir(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR); + +require_once dirname(__FILE__) . '/Request2Test.php'; +require_once dirname(__FILE__) . '/ObserverTest.php'; +require_once dirname(__FILE__) . '/Request2/AllTests.php'; + +class HTTP_Request2_AllTests +{ + public static function main() + { + PHPUnit_TextUI_TestRunner::run(self::suite()); + } + + public static function suite() + { + $suite = new PHPUnit_Framework_TestSuite('HTTP_Request2 package'); + + $suite->addTest(Request2_AllTests::suite()); + $suite->addTestSuite('HTTP_Request2Test'); + $suite->addTestSuite('HTTP_Request2_ObserverTest'); + + return $suite; + } +} + +if (PHPUnit_MAIN_METHOD == 'HTTP_Request2_AllTests::main') { + HTTP_Request2_AllTests::main(); +} +?> \ No newline at end of file diff --git a/lib/tests/HTTP_Request2/HTTP/tests/ObserverTest.php b/lib/tests/HTTP_Request2/HTTP/tests/ObserverTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b5578d658585e26e82be1af4b711e984b508abbd --- /dev/null +++ b/lib/tests/HTTP_Request2/HTTP/tests/ObserverTest.php @@ -0,0 +1,120 @@ +<?php +/** + * Unit tests for HTTP_Request2 package + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: ObserverTest.php 290192 2009-11-03 21:29:32Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Class representing a HTTP request + */ +require_once 'HTTP/Request2.php'; + +/** + * PHPUnit Test Case + */ +require_once 'PHPUnit/Framework/TestCase.php'; + +/** + * Mock observer + */ +class HTTP_Request2_MockObserver implements SplObserver +{ + public $calls = 0; + + public $event; + + public function update (SplSubject $subject) + { + $this->calls++; + $this->event = $subject->getLastEvent(); + } +} + +/** + * Unit test for subject-observer pattern implementation in HTTP_Request2 + */ +class HTTP_Request2_ObserverTest extends PHPUnit_Framework_TestCase +{ + public function testSetLastEvent() + { + $request = new HTTP_Request2(); + $observer = new HTTP_Request2_MockObserver(); + $request->attach($observer); + + $request->setLastEvent('foo', 'bar'); + $this->assertEquals(1, $observer->calls); + $this->assertEquals(array('name' => 'foo', 'data' => 'bar'), $observer->event); + + $request->setLastEvent('baz'); + $this->assertEquals(2, $observer->calls); + $this->assertEquals(array('name' => 'baz', 'data' => null), $observer->event); + } + + public function testAttachOnlyOnce() + { + $request = new HTTP_Request2(); + $observer = new HTTP_Request2_MockObserver(); + $observer2 = new HTTP_Request2_MockObserver(); + $request->attach($observer); + $request->attach($observer2); + $request->attach($observer); + + $request->setLastEvent('event', 'data'); + $this->assertEquals(1, $observer->calls); + $this->assertEquals(1, $observer2->calls); + } + + public function testDetach() + { + $request = new HTTP_Request2(); + $observer = new HTTP_Request2_MockObserver(); + $observer2 = new HTTP_Request2_MockObserver(); + + $request->attach($observer); + $request->detach($observer2); // should not be a error + $request->setLastEvent('first'); + + $request->detach($observer); + $request->setLastEvent('second'); + $this->assertEquals(1, $observer->calls); + $this->assertEquals(array('name' => 'first', 'data' => null), $observer->event); + } +} +?> \ No newline at end of file diff --git a/lib/tests/HTTP_Request2/HTTP/tests/Request2/Adapter/AllTests.php b/lib/tests/HTTP_Request2/HTTP/tests/Request2/Adapter/AllTests.php new file mode 100644 index 0000000000000000000000000000000000000000..05f9f8764a387e490a6ad4d15de1bb0656fb91b4 --- /dev/null +++ b/lib/tests/HTTP_Request2/HTTP/tests/Request2/Adapter/AllTests.php @@ -0,0 +1,73 @@ +<?php +/** + * Unit tests for HTTP_Request2 package + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: AllTests.php 290192 2009-11-03 21:29:32Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +if (!defined('PHPUnit_MAIN_METHOD')) { + define('PHPUnit_MAIN_METHOD', 'Request2_Adapter_AllTests::main'); +} + +require_once 'PHPUnit/Framework.php'; +require_once 'PHPUnit/TextUI/TestRunner.php'; + +require_once dirname(__FILE__) . '/MockTest.php'; + +class Request2_Adapter_AllTests +{ + public static function main() + { + PHPUnit_TextUI_TestRunner::run(self::suite()); + } + + public static function suite() + { + $suite = new PHPUnit_Framework_TestSuite('HTTP_Request2 package - Request2 - Adapter'); + + $suite->addTestSuite('HTTP_Request2_Adapter_MockTest'); + + return $suite; + } +} + +if (PHPUnit_MAIN_METHOD == 'Request2_Adapter_AllTests::main') { + Request2_Adapter_AllTests::main(); +} +?> diff --git a/lib/tests/HTTP_Request2/HTTP/tests/Request2/Adapter/MockTest.php b/lib/tests/HTTP_Request2/HTTP/tests/Request2/Adapter/MockTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b07036fb62d8e5772e215cacf150eaa5447a6870 --- /dev/null +++ b/lib/tests/HTTP_Request2/HTTP/tests/Request2/Adapter/MockTest.php @@ -0,0 +1,147 @@ +<?php +/** + * Unit tests for HTTP_Request2 package + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: MockTest.php 290192 2009-11-03 21:29:32Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Class representing a HTTP request + */ +require_once 'HTTP/Request2.php'; + +/** + * Mock adapter intended for testing + */ +require_once 'HTTP/Request2/Adapter/Mock.php'; + +/** + * PHPUnit Test Case + */ +require_once 'PHPUnit/Framework/TestCase.php'; + +/** + * Unit test for HTTP_Request2_Response class + */ +class HTTP_Request2_Adapter_MockTest extends PHPUnit_Framework_TestCase +{ + public function testDefaultResponse() + { + $req = new HTTP_Request2('http://www.example.com/', HTTP_Request2::METHOD_GET, + array('adapter' => 'mock')); + $response = $req->send(); + $this->assertEquals(400, $response->getStatus()); + $this->assertEquals(0, count($response->getHeader())); + $this->assertEquals('', $response->getBody()); + } + + public function testResponseFromString() + { + $mock = new HTTP_Request2_Adapter_Mock(); + $mock->addResponse( + "HTTP/1.1 200 OK\r\n" . + "Content-Type: text/plain; charset=iso-8859-1\r\n" . + "\r\n" . + "This is a string" + ); + $req = new HTTP_Request2('http://www.example.com/'); + $req->setAdapter($mock); + + $response = $req->send(); + $this->assertEquals(200, $response->getStatus()); + $this->assertEquals(1, count($response->getHeader())); + $this->assertEquals('This is a string', $response->getBody()); + } + + public function testResponseFromFile() + { + $mock = new HTTP_Request2_Adapter_Mock(); + $mock->addResponse(fopen(dirname(dirname(dirname(__FILE__))) . + '/_files/response_headers', 'rb')); + + $req = new HTTP_Request2('http://www.example.com/'); + $req->setAdapter($mock); + + $response = $req->send(); + $this->assertEquals(200, $response->getStatus()); + $this->assertEquals(7, count($response->getHeader())); + $this->assertEquals('Nothing to see here, move along.', $response->getBody()); + } + + public function testResponsesQueue() + { + $mock = new HTTP_Request2_Adapter_Mock(); + $mock->addResponse( + "HTTP/1.1 301 Over there\r\n" . + "Location: http://www.example.com/newpage.html\r\n" . + "\r\n" . + "The document is over there" + ); + $mock->addResponse( + "HTTP/1.1 200 OK\r\n" . + "Content-Type: text/plain; charset=iso-8859-1\r\n" . + "\r\n" . + "This is a string" + ); + + $req = new HTTP_Request2('http://www.example.com/'); + $req->setAdapter($mock); + $this->assertEquals(301, $req->send()->getStatus()); + $this->assertEquals(200, $req->send()->getStatus()); + $this->assertEquals(400, $req->send()->getStatus()); + } + + public function testResponseException() + { + $mock = new HTTP_Request2_Adapter_Mock(); + $mock->addResponse( + new HTTP_Request2_Exception('Shit happens') + ); + $req = new HTTP_Request2('http://www.example.com/'); + $req->setAdapter($mock); + try { + $req->send(); + } catch (Exception $e) { + $this->assertEquals('Shit happens', $e->getMessage()); + return; + } + $this->fail('Expected HTTP_Request2_Exception was not thrown'); + } +} +?> diff --git a/lib/tests/HTTP_Request2/HTTP/tests/Request2/AllTests.php b/lib/tests/HTTP_Request2/HTTP/tests/Request2/AllTests.php new file mode 100644 index 0000000000000000000000000000000000000000..826b77d4f611837bcec680e0e898ca761c120fa2 --- /dev/null +++ b/lib/tests/HTTP_Request2/HTTP/tests/Request2/AllTests.php @@ -0,0 +1,77 @@ +<?php +/** + * Unit tests for HTTP_Request2 package + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: AllTests.php 290192 2009-11-03 21:29:32Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +if (!defined('PHPUnit_MAIN_METHOD')) { + define('PHPUnit_MAIN_METHOD', 'Request2_AllTests::main'); +} + +require_once 'PHPUnit/Framework.php'; +require_once 'PHPUnit/TextUI/TestRunner.php'; + +require_once dirname(__FILE__) . '/MultipartBodyTest.php'; +require_once dirname(__FILE__) . '/ResponseTest.php'; +require_once dirname(__FILE__) . '/Adapter/AllTests.php'; + +class Request2_AllTests +{ + public static function main() + { + PHPUnit_TextUI_TestRunner::run(self::suite()); + } + + public static function suite() + { + $suite = new PHPUnit_Framework_TestSuite('HTTP_Request2 package - Request2'); + + $suite->addTestSuite('HTTP_Request2_MultipartBodyTest'); + $suite->addTestSuite('HTTP_Request2_ResponseTest'); + $suite->addTest(Request2_Adapter_AllTests::suite()); + + return $suite; + } +} + +if (PHPUnit_MAIN_METHOD == 'Request2_AllTests::main') { + Request2_AllTests::main(); +} +?> \ No newline at end of file diff --git a/lib/tests/HTTP_Request2/HTTP/tests/Request2/MultipartBodyTest.php b/lib/tests/HTTP_Request2/HTTP/tests/Request2/MultipartBodyTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9fd5d64a4d81db747ce35529bbb94e4d1d40905f --- /dev/null +++ b/lib/tests/HTTP_Request2/HTTP/tests/Request2/MultipartBodyTest.php @@ -0,0 +1,109 @@ +<?php +/** + * Unit tests for HTTP_Request2 package + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: MultipartBodyTest.php 290192 2009-11-03 21:29:32Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Class representing a HTTP request + */ +require_once 'HTTP/Request2.php'; + +/** + * PHPUnit Test Case + */ +require_once 'PHPUnit/Framework/TestCase.php'; + +/** + * Unit test for HTTP_Request2_MultipartBody class + */ +class HTTP_Request2_MultipartBodyTest extends PHPUnit_Framework_TestCase +{ + public function testUploadSimple() + { + $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST); + $body = $req->addPostParameter('foo', 'I am a parameter') + ->addUpload('upload', dirname(dirname(__FILE__)) . '/_files/plaintext.txt') + ->getBody(); + + $this->assertTrue($body instanceof HTTP_Request2_MultipartBody); + $asString = $body->__toString(); + $boundary = $body->getBoundary(); + $this->assertEquals($body->getLength(), strlen($asString)); + $this->assertContains('This is a test.', $asString); + $this->assertContains('I am a parameter', $asString); + $this->assertRegexp("!--{$boundary}--\r\n$!", $asString); + } + + public function testStreaming() + { + $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST); + $body = $req->addPostParameter('foo', 'I am a parameter') + ->addUpload('upload', dirname(dirname(__FILE__)) . '/_files/plaintext.txt') + ->getBody(); + $asString = ''; + while ($part = $body->read(10)) { + $asString .= $part; + } + $this->assertEquals($body->getLength(), strlen($asString)); + $this->assertContains('This is a test.', $asString); + $this->assertContains('I am a parameter', $asString); + } + + public function testUploadArray() + { + $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST); + $body = $req->addUpload('upload', array( + array(dirname(dirname(__FILE__)) . '/_files/plaintext.txt', 'bio.txt', 'text/plain'), + array(dirname(dirname(__FILE__)) . '/_files/empty.gif', 'photo.gif', 'image/gif') + )) + ->getBody(); + $asString = $body->__toString(); + $this->assertContains(file_get_contents(dirname(dirname(__FILE__)) . '/_files/empty.gif'), $asString); + $this->assertContains('name="upload[0]"; filename="bio.txt"', $asString); + $this->assertContains('name="upload[1]"; filename="photo.gif"', $asString); + + $body2 = $req->setConfig(array('use_brackets' => false))->getBody(); + $asString = $body2->__toString(); + $this->assertContains('name="upload"; filename="bio.txt"', $asString); + $this->assertContains('name="upload"; filename="photo.gif"', $asString); + } +} +?> \ No newline at end of file diff --git a/lib/tests/HTTP_Request2/HTTP/tests/Request2/ResponseTest.php b/lib/tests/HTTP_Request2/HTTP/tests/Request2/ResponseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b605e066bf704b49c2eec5cedff7d18e9b708de8 --- /dev/null +++ b/lib/tests/HTTP_Request2/HTTP/tests/Request2/ResponseTest.php @@ -0,0 +1,149 @@ +<?php +/** + * Unit tests for HTTP_Request2 package + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: ResponseTest.php 290192 2009-11-03 21:29:32Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Class representing a HTTP response + */ +require_once 'HTTP/Request2/Response.php'; + +/** + * PHPUnit Test Case + */ +require_once 'PHPUnit/Framework/TestCase.php'; + +/** + * Unit test for HTTP_Request2_Response class + */ +class HTTP_Request2_ResponseTest extends PHPUnit_Framework_TestCase +{ + public function testParseStatusLine() + { + $response = new HTTP_Request2_Response('HTTP/1.1 200 OK'); + $this->assertEquals('1.1', $response->getVersion()); + $this->assertEquals(200, $response->getStatus()); + $this->assertEquals('OK', $response->getReasonPhrase()); + + $response2 = new HTTP_Request2_Response('HTTP/1.2 222 Nishtyak!'); + $this->assertEquals('1.2', $response2->getVersion()); + $this->assertEquals(222, $response2->getStatus()); + $this->assertEquals('Nishtyak!', $response2->getReasonPhrase()); + + try { + $response3 = new HTTP_Request2_Response('Invalid status line'); + } catch (HTTP_Request2_Exception $e) { + return; + } + $this->fail('Expected HTTP_Request2_Exception was not thrown'); + } + + public function testParseHeaders() + { + $response = $this->readResponseFromFile('response_headers'); + $this->assertEquals(7, count($response->getHeader())); + $this->assertEquals('PHP/6.2.2', $response->getHeader('X-POWERED-BY')); + $this->assertEquals('text/html; charset=windows-1251', $response->getHeader('cOnTeNt-TyPe')); + $this->assertEquals('accept-charset, user-agent', $response->getHeader('vary')); + } + + public function testParseCookies() + { + $response = $this->readResponseFromFile('response_cookies'); + $cookies = $response->getCookies(); + $this->assertEquals(4, count($cookies)); + $expected = array( + array('name' => 'foo', 'value' => 'bar', 'expires' => null, + 'domain' => null, 'path' => null, 'secure' => false), + array('name' => 'PHPSESSID', 'value' => '1234567890abcdef1234567890abcdef', + 'expires' => null, 'domain' => null, 'path' => '/', 'secure' => true), + array('name' => 'A', 'value' => 'B=C', 'expires' => null, + 'domain' => null, 'path' => null, 'secure' => false), + array('name' => 'baz', 'value' => '%20a%20value', 'expires' => 'Sun, 03 Jan 2010 03:04:05 GMT', + 'domain' => 'pear.php.net', 'path' => null, 'secure' => false), + ); + foreach ($cookies as $k => $cookie) { + $this->assertEquals($expected[$k], $cookie); + } + } + + public function testGzipEncoding() + { + $response = $this->readResponseFromFile('response_gzip'); + $this->assertEquals('0e964e9273c606c46afbd311b5ad4d77', md5($response->getBody())); + + try { + $response = $this->readResponseFromFile('response_gzip_broken'); + $body = $response->getBody(); + } catch (HTTP_Request2_Exception $e) { + return; + } + $this->fail('Expected HTTP_Request2_Exception was not thrown'); + } + + public function testDeflateEncoding() + { + $response = $this->readResponseFromFile('response_deflate'); + $this->assertEquals('0e964e9273c606c46afbd311b5ad4d77', md5($response->getBody())); + } + + public function testBug15305() + { + $response = $this->readResponseFromFile('bug_15305'); + $this->assertEquals('c8c5088fc8a7652afef380f086c010a6', md5($response->getBody())); + } + + protected function readResponseFromFile($filename) + { + $fp = fopen(dirname(dirname(__FILE__)) . '/_files/' . $filename, 'rb'); + $response = new HTTP_Request2_Response(fgets($fp)); + do { + $headerLine = fgets($fp); + $response->parseHeaderLine($headerLine); + } while ('' != trim($headerLine)); + + while (!feof($fp)) { + $response->appendBody(fread($fp, 1024)); + } + return $response; + } +} +?> \ No newline at end of file diff --git a/lib/tests/HTTP_Request2/HTTP/tests/Request2Test.php b/lib/tests/HTTP_Request2/HTTP/tests/Request2Test.php new file mode 100644 index 0000000000000000000000000000000000000000..34d30a5309d55d4de9fd9188eb627964d1fb35b4 --- /dev/null +++ b/lib/tests/HTTP_Request2/HTTP/tests/Request2Test.php @@ -0,0 +1,297 @@ +<?php +/** + * Unit tests for HTTP_Request2 package + * + * PHP version 5 + * + * LICENSE: + * + * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request2 + * @author Alexey Borzov <avb@php.net> + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version SVN: $Id: Request2Test.php 290921 2009-11-18 17:31:58Z avb $ + * @link http://pear.php.net/package/HTTP_Request2 + */ + +/** + * Class representing a HTTP request + */ +require_once 'HTTP/Request2.php'; + +/** + * PHPUnit Test Case + */ +require_once 'PHPUnit/Framework/TestCase.php'; + +/** + * Unit test for HTTP_Request2 class + */ +class HTTP_Request2Test extends PHPUnit_Framework_TestCase +{ + public function testConstructorSetsDefaults() + { + $url = new Net_URL2('http://www.example.com/foo'); + $req = new HTTP_Request2($url, HTTP_Request2::METHOD_POST, array('connect_timeout' => 666)); + + $this->assertSame($url, $req->getUrl()); + $this->assertEquals(HTTP_Request2::METHOD_POST, $req->getMethod()); + $this->assertEquals(666, $req->getConfig('connect_timeout')); + } + + public function testSetUrl() + { + $urlString = 'http://www.example.com/foo/bar.php'; + $url = new Net_URL2($urlString); + + $req1 = new HTTP_Request2(); + $req1->setUrl($url); + $this->assertSame($url, $req1->getUrl()); + + $req2 = new HTTP_Request2(); + $req2->setUrl($urlString); + $this->assertType('Net_URL2', $req2->getUrl()); + $this->assertEquals($urlString, $req2->getUrl()->getUrl()); + + try { + $req3 = new HTTP_Request2(); + $req3->setUrl(array('This will cause an error')); + } catch (HTTP_Request2_Exception $e) { + return; + } + $this->fail('Expected HTTP_Request2_Exception was not thrown'); + } + + public function testConvertUserinfoToAuth() + { + $req = new HTTP_Request2(); + $req->setUrl('http://foo:b%40r@www.example.com/'); + + $this->assertEquals('', (string)$req->getUrl()->getUserinfo()); + $this->assertEquals( + array('user' => 'foo', 'password' => 'b@r', 'scheme' => HTTP_Request2::AUTH_BASIC), + $req->getAuth() + ); + } + + public function testSetMethod() + { + $req = new HTTP_Request2(); + $req->setMethod(HTTP_Request2::METHOD_PUT); + $this->assertEquals(HTTP_Request2::METHOD_PUT, $req->getMethod()); + try { + $req->setMethod('Invalid method'); + } catch (HTTP_Request2_Exception $e) { + return; + } + $this->fail('Expected HTTP_Request2_Exception was not thrown'); + } + + public function testSetAndGetConfig() + { + $req = new HTTP_Request2(); + $this->assertArrayHasKey('connect_timeout', $req->getConfig()); + + $req->setConfig(array('connect_timeout' => 123)); + $this->assertEquals(123, $req->getConfig('connect_timeout')); + try { + $req->setConfig(array('foo' => 'unknown parameter')); + } catch (HTTP_Request2_Exception $e) { + try { + $req->getConfig('bar'); + } catch (HTTP_Request2_Exception $e) { + return; + } + } + $this->fail('Expected HTTP_Request2_Exception was not thrown'); + } + + public function testHeaders() + { + $req = new HTTP_Request2(); + $autoHeaders = $req->getHeaders(); + + $req->setHeader('Foo', 'Bar'); + $req->setHeader('Foo-Bar: value'); + $req->setHeader(array('Another-Header' => 'another value', 'Yet-Another: other_value')); + $this->assertEquals( + array('foo-bar' => 'value', 'another-header' => 'another value', + 'yet-another' => 'other_value', 'foo' => 'Bar') + $autoHeaders, + $req->getHeaders() + ); + + $req->setHeader('FOO-BAR'); + $req->setHeader(array('aNOTHER-hEADER')); + $this->assertEquals( + array('yet-another' => 'other_value', 'foo' => 'Bar') + $autoHeaders, + $req->getHeaders() + ); + + try { + $req->setHeader('Invalid header', 'value'); + } catch (HTTP_Request2_Exception $e) { + return; + } + $this->fail('Expected HTTP_Request2_Exception was not thrown'); + } + + public function testBug15937() + { + $req = new HTTP_Request2(); + $autoHeaders = $req->getHeaders(); + + $req->setHeader('Expect: '); + $req->setHeader('Foo', ''); + $this->assertEquals( + array('expect' => '', 'foo' => '') + $autoHeaders, + $req->getHeaders() + ); + } + + public function testCookies() + { + $req = new HTTP_Request2(); + $req->addCookie('name', 'value'); + $req->addCookie('foo', 'bar'); + $headers = $req->getHeaders(); + $this->assertEquals('name=value; foo=bar', $headers['cookie']); + + try { + $req->addCookie('invalid cookie', 'value'); + } catch (HTTP_Request2_Exception $e) { + return; + } + $this->fail('Expected HTTP_Request2_Exception was not thrown'); + } + + public function testPlainBody() + { + $req = new HTTP_Request2(); + $req->setBody('A string'); + $this->assertEquals('A string', $req->getBody()); + + $req->setBody(dirname(__FILE__) . '/_files/plaintext.txt', true); + $headers = $req->getHeaders(); + $this->assertContains( + $headers['content-type'], + array('text/plain', 'application/octet-stream') + ); + $this->assertEquals('This is a test.', fread($req->getBody(), 1024)); + + try { + $req->setBody('missing file', true); + } catch (HTTP_Request2_Exception $e) { + return; + } + $this->fail('Expected HTTP_Request2_Exception was not thrown'); + } + + public function testUrlencodedBody() + { + $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST); + $req->addPostParameter('foo', 'bar'); + $req->addPostParameter(array('baz' => 'quux')); + $req->addPostParameter('foobar', array('one', 'two')); + $this->assertEquals( + 'foo=bar&baz=quux&foobar%5B0%5D=one&foobar%5B1%5D=two', + $req->getBody() + ); + + $req->setConfig(array('use_brackets' => false)); + $this->assertEquals( + 'foo=bar&baz=quux&foobar=one&foobar=two', + $req->getBody() + ); + } + + public function testRequest15368() + { + $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST); + $req->addPostParameter('foo', 'te~st'); + $this->assertContains('~', $req->getBody()); + } + + public function testUpload() + { + $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST); + $req->addUpload('upload', dirname(__FILE__) . '/_files/plaintext.txt'); + + $headers = $req->getHeaders(); + $this->assertEquals('multipart/form-data', $headers['content-type']); + + try { + $req->addUpload('upload_2', 'missing file'); + } catch (HTTP_Request2_Exception $e) { + return; + } + $this->fail('Expected HTTP_Request2_Exception was not thrown'); + } + + public function testPropagateUseBracketsToNetURL2() + { + $req = new HTTP_Request2('http://www.example.com/', HTTP_Request2::METHOD_GET, + array('use_brackets' => false)); + $req->getUrl()->setQueryVariable('foo', array('bar', 'baz')); + $this->assertEquals('http://www.example.com/?foo=bar&foo=baz', $req->getUrl()->__toString()); + + $req->setConfig('use_brackets', true)->setUrl('http://php.example.com/'); + $req->getUrl()->setQueryVariable('foo', array('bar', 'baz')); + $this->assertEquals('http://php.example.com/?foo[0]=bar&foo[1]=baz', $req->getUrl()->__toString()); + } + + public function testSetBodyRemovesPostParameters() + { + $req = new HTTP_Request2('http://www.example.com/', HTTP_Request2::METHOD_POST); + $req->addPostParameter('foo', 'bar'); + $req->setBody(''); + $this->assertEquals('', $req->getBody()); + } + + public function testPostParametersPrecedeSetBodyForPost() + { + $req = new HTTP_Request2('http://www.example.com/', HTTP_Request2::METHOD_POST); + $req->setBody('Request body'); + $req->addPostParameter('foo', 'bar'); + + $this->assertEquals('foo=bar', $req->getBody()); + + $req->setMethod(HTTP_Request2::METHOD_PUT); + $this->assertEquals('Request body', $req->getBody()); + } + + public function testSetMultipartBody() + { + $req = new HTTP_Request2('http://www.example.com/', HTTP_Request2::METHOD_POST); + $body = new HTTP_Request2_MultipartBody(array('foo' => 'bar'), array()); + $req->setBody($body); + $this->assertSame($body, $req->getBody()); + } +} +?> \ No newline at end of file diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/bug_15305 b/lib/tests/HTTP_Request2/HTTP/tests/_files/bug_15305 new file mode 100644 index 0000000000000000000000000000000000000000..bbf6c70006b8062fd656063ee9cbbff16edf7f04 Binary files /dev/null and b/lib/tests/HTTP_Request2/HTTP/tests/_files/bug_15305 differ diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/empty.gif b/lib/tests/HTTP_Request2/HTTP/tests/_files/empty.gif new file mode 100644 index 0000000000000000000000000000000000000000..1d11fa9ada9e93505b3d736acb204083f45d5fbf Binary files /dev/null and b/lib/tests/HTTP_Request2/HTTP/tests/_files/empty.gif differ diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/plaintext.txt b/lib/tests/HTTP_Request2/HTTP/tests/_files/plaintext.txt new file mode 100644 index 0000000000000000000000000000000000000000..273c1a9ffdc201dbca7d19b275bf1cbc88aaa4cb --- /dev/null +++ b/lib/tests/HTTP_Request2/HTTP/tests/_files/plaintext.txt @@ -0,0 +1 @@ +This is a test. \ No newline at end of file diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/response_cookies b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_cookies new file mode 100644 index 0000000000000000000000000000000000000000..8f0ab6b64d490cd12b0adc653e03211a44822b4b --- /dev/null +++ b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_cookies @@ -0,0 +1,13 @@ +HTTP/1.1 200 OK +Date: Fri, 01 Jan 2010 02:03:04 GMT +Server: Apache/3.0.1 (Unix) +X-Powered-By: PHP/6.2.2 +Set-Cookie: foo=bar +Set-Cookie: PHPSESSID=1234567890abcdef1234567890abcdef; path=/; secure +Set-Cookie: A=B=C +Set-Cookie: baz=%20a%20value; expires=Sun, 03 Jan 2010 03:04:05 GMT; domain=pear.php.net +Content-Type: text/html; charset=windows-1251 +Content-Length: 32 +Connection: close + +Nothing to see here, move along. \ No newline at end of file diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/response_deflate b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_deflate new file mode 100644 index 0000000000000000000000000000000000000000..e4e8adfd0d46af40addef96b0e95dad6243e6232 Binary files /dev/null and b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_deflate differ diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/response_gzip b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_gzip new file mode 100644 index 0000000000000000000000000000000000000000..79ad3b6eaac6737c9ae4a577347cb52e26ea5209 Binary files /dev/null and b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_gzip differ diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/response_gzip_broken b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_gzip_broken new file mode 100644 index 0000000000000000000000000000000000000000..0df0d1522f8a377ac905b20db95eb29dc1428493 Binary files /dev/null and b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_gzip_broken differ diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/response_headers b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_headers new file mode 100644 index 0000000000000000000000000000000000000000..f60787bd566db41bc4680d2e6a93aba607e3a34f --- /dev/null +++ b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_headers @@ -0,0 +1,12 @@ +HTTP/1.1 200 OK +Date: Fri, 01 Jan 2010 02:03:04 GMT +Vary: accept-charset +Server: Apache/3.0.1 (Unix) +X-Powered-By: PHP/6.2.2 +Vary: user-agent +Content-Type: text/html; + charset=windows-1251 +Content-Length: 32 +Connection: close + +Nothing to see here, move along. \ No newline at end of file diff --git a/lib/tests/Services_W3C_HTMLValidator/Services/W3C/tests/AllTests.php b/lib/tests/Services_W3C_HTMLValidator/Services/W3C/tests/AllTests.php new file mode 100644 index 0000000000000000000000000000000000000000..592d9215bf38e346d6a1698c377bb72e7f5301ef --- /dev/null +++ b/lib/tests/Services_W3C_HTMLValidator/Services/W3C/tests/AllTests.php @@ -0,0 +1,68 @@ +<?php +/** + * Test suite for Services_W3C_HTMLValidator + * + * PHP versions 5 + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @version CVS: $id$ + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ + +if (!defined('PHPUnit_MAIN_METHOD')) { + define('PHPUnit_MAIN_METHOD', 'Services_W3C_HTMLValidator_AllTests::main'); +} + +require_once 'PHPUnit/Framework/TestSuite.php'; +require_once 'PHPUnit/TextUI/TestRunner.php'; + + +require_once 'Services/W3C/HTMLValidatorTest.php'; + +/** + * Class for running all test suites for Services_W3C_HTMLvalidator. + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ + +class Services_W3C_HTMLValidator_AllTests +{ + /** + * Runs the test suite. + * + * @return unknown + */ + public static function main() + { + + PHPUnit_TextUI_TestRunner::run(self::suite()); + } + + /** + * Adds the Services_W3C_HTMLValidatorTest suite. + * + * @return $suite + */ + public static function suite() + { + $suite = new PHPUnit_Framework_TestSuite('Services_W3C_HTMLValidator tests'); + /** Add testsuites, if there is. */ + $suite->addTestSuite('Services_W3C_HTMLValidatorTest'); + + return $suite; + } +} + +if (PHPUnit_MAIN_METHOD == 'Services_W3C_HTMLValidator_AllTests::main') { + Services_W3C_HTMLValidator_AllTests::main(); +} +?> \ No newline at end of file diff --git a/lib/tests/Services_W3C_HTMLValidator/Services/W3C/tests/Services/W3C/HTMLValidatorTest.php b/lib/tests/Services_W3C_HTMLValidator/Services/W3C/tests/Services/W3C/HTMLValidatorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7c0fb70f2eee6b6935d7b78e19c67801791988e1 --- /dev/null +++ b/lib/tests/Services_W3C_HTMLValidator/Services/W3C/tests/Services/W3C/HTMLValidatorTest.php @@ -0,0 +1,205 @@ +<?php +/** + * Test suite for the Services_W3C_HTMLValidator class + * + * PHP versions 5 + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @version CVS: $id$ + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ +// Call Services_W3C_HTMLValidatorTest::main() if executed directly. +if (!defined("PHPUnit_MAIN_METHOD")) { + define("PHPUnit_MAIN_METHOD", "Services_W3C_HTMLValidatorTest::main"); +} + +require_once "PHPUnit/Framework/TestCase.php"; +require_once "PHPUnit/Framework/TestSuite.php"; + +require_once 'Services/W3C/HTMLValidator.php'; + +/** + * Test class for Services_W3C_HTMLValidator. + * Generated by PHPUnit_Util_Skeleton on 2007-04-09 at 10:51:02. + * + * @category Services + * @package Services_W3C_HTMLValidator + * @author Brett Bieber <brett.bieber@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php BSD + * @link http://pear.php.net/package/Services_W3C_HTMLValidator + * @since File available since Release 0.2.0 + */ +class Services_W3C_HTMLValidatorTest extends PHPUnit_Framework_TestCase +{ + /** + * Runs the test methods of this class. + * + * @access public + * @static + * @return void + */ + public static function main() + { + include_once "PHPUnit/TextUI/TestRunner.php"; + + $suite = new PHPUnit_Framework_TestSuite("Services_W3C_HTMLValidatorTest"); + $result = PHPUnit_TextUI_TestRunner::run($suite); + } + + /** + * Sets up the fixture, for example, open a network connection. + * This method is called before a test is executed. + * + * @access protected + * @return void + */ + protected function setUp() + { + } + + /** + * Tears down the fixture, for example, close a network connection. + * This method is called after a test is executed. + * + * @access protected + * @return void + */ + protected function tearDown() + { + } + + /** + * Tests setting the options. + * + * @return void + */ + public function testSetOptions() + { + $test = new Services_W3C_HTMLValidator(array( + 'uri'=>'foo', + 'validator_uri'=>'bar')); + // Test that value was set. + $this->assertEquals($test->uri, 'foo'); + $this->assertEquals($test->validator_uri, 'bar'); + } + + /** + * Tests validating a URI + * + * @return void + */ + public function testValidate() + { + $uri = 'http://www.w3.org/'; + $v = new Services_W3C_HTMLValidator(); + $r = $v->validate($uri); + $this->assertEquals(get_class($r), 'Services_W3C_HTMLValidator_Response'); + $this->assertEquals($r->isValid(), true); + $this->assertEquals(count($r->errors), 0); + $this->assertEquals($r->uri, $uri); + $this->assertEquals($r->checkedby.'check', $v->validator_uri); + } + + /** + * Tests validating a local file. + * + * @return void + */ + public function testValidateFile() + { + $v = new Services_W3C_HTMLValidator(); + $doc_dir = dirname(realpath(__FILE__)); + $file = '/../../../docs/examples/example.html'; + $r = $v->validateFile($doc_dir.$file); + $this->assertEquals(get_class($r), 'Services_W3C_HTMLValidator_Response'); + $this->assertEquals($r->isValid(), false); + $this->assertEquals($r->charset, 'utf-8'); + $this->assertEquals(count($r->errors), 37); + $this->assertEquals(get_class($r->errors[0]), + 'Services_W3C_HTMLValidator_Error'); + } + + /** + * Tests validating a fragment of html + * + * @return void + */ + public function testValidateFragment() + { + $v = new Services_W3C_HTMLValidator(); + $r = $v->validateFragment('<!DOCTYPE html PUBLIC '. + '"-//W3C//DTD XHTML 1.0 Strict//EN" +"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head><title>HTML Fragment</title></head> + <body><p>TEST!</p></body> +</html>'); + $this->assertEquals(get_class($r), 'Services_W3C_HTMLValidator_Response'); + $this->assertEquals($r->isValid(), true); + $this->assertEquals($r->charset, 'utf-8'); + $this->assertEquals($r->uri, 'upload://Form Submission'); + } + + /** + * Tests parsing the raw soap12 response. + * + * @return void + */ + public function testParseSOAP12Response() + { + $v = new Services_W3C_HTMLValidator(); + $r = $v->parseSOAP12Response('<?xml version="1.0" encoding="UTF-8"?> +<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> +<env:Body> +<m:markupvalidationresponse +env:encodingStyle="http://www.w3.org/2003/05/soap-encoding" +xmlns:m="http://www.w3.org/2005/10/markup-validator"> + <m:uri>http://qa-dev.w3.org/wmvs/HEAD/dev/tests/xhtml1-bogus-element.html</m:uri> + <m:checkedby>http://validator.w3.org/</m:checkedby> + <m:doctype>-//W3C//DTD XHTML 1.0 Transitional//EN</m:doctype> + <m:charset>utf-8</m:charset> + <m:validity>false</m:validity> + <m:errors> + <m:errorcount>1</m:errorcount> + <m:errorlist> + + <m:error> + <m:line>13</m:line> + <m:col>6</m:col> + <m:message>element "foo" undefined</m:message> + </m:error> + + </m:errorlist> + </m:errors> + <m:warnings> + <m:warningcount>0</m:warningcount> + <m:warninglist> + + + </m:warninglist> + </m:warnings> +</m:markupvalidationresponse> +</env:Body> +</env:Envelope> +'); + $this->assertEquals(get_class($r), 'Services_W3C_HTMLValidator_Response'); + $this->assertEquals(serialize($r), 'O:35:"Services_W3C_HTMLValidator_Respo'. + 'nse":7:{s:3:"uri";s:66:"http://qa-dev.w3.org/wmvs/HEAD/dev/tests/xhtml1-b'. + 'ogus-element.html";s:9:"checkedby";s:24:"http://validator.w3.org/";s:7:"d'. + 'octype";s:38:"-//W3C//DTD XHTML 1.0 Transitional//EN";s:7:"charset";s:5:"'. + 'utf-8";s:8:"validity";b:0;s:6:"errors";a:1:{i:0;O:32:"Services_W3C_HTMLVa'. + 'lidator_Error":6:{s:4:"line";s:2:"13";s:3:"col";s:1:"6";s:7:"message";s:2'. + '3:"element "foo" undefined";s:9:"messageid";N;s:11:"explanation";N;s:6:"s'. + 'ource";N;}}s:8:"warnings";a:0:{}}'); + } +} + +// Call Services_W3C_HTMLValidatorTest::main() if file is executed directly. +if (PHPUnit_MAIN_METHOD == "Services_W3C_HTMLValidatorTest::main") { + Services_W3C_HTMLValidatorTest::main(); +} +?> diff --git a/www/index.php b/www/index.php new file mode 100644 index 0000000000000000000000000000000000000000..94a270f4081401b5fda9f60cedd0a1eb14c6e3db --- /dev/null +++ b/www/index.php @@ -0,0 +1,7 @@ +<?php + + +require_once 'UNL/Autoload.php'; +$page = UNL_Templates::factory('Document'); + +echo $page;