diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ce3aa65
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+phpunit.xml
diff --git a/.gitmodules b/.gitmodules
index 593766f..36dbd34 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,12 +10,12 @@
[submodule "vendor/Symfony/Component/Process"]
path = vendor/Symfony/Component/Process
url = https://github.com/symfony/Process
-[submodule "vendor/zend"]
- path = vendor/zend
- url = https://github.com/zendframework/zf2
[submodule "vendor/Symfony/Component/ClassLoader"]
path = vendor/Symfony/Component/ClassLoader
url = https://github.com/symfony/ClassLoader
[submodule "vendor/Symfony/Component/Finder"]
path = vendor/Symfony/Component/Finder
url = https://github.com/symfony/Finder
+[submodule "vendor/Guzzle"]
+ path = vendor/Guzzle
+ url = git://github.com/guzzle/guzzle.git
diff --git a/README.md b/README.md
index 363fc6b..30ec098 100644
--- a/README.md
+++ b/README.md
@@ -71,7 +71,7 @@ Goutte is a thin wrapper around the following fine PHP libraries:
* Symfony Components: BrowserKit, ClassLoader, CssSelector, DomCrawler, Finder, and Process
- * Zend libraries: Date, Uri, Http, and Validator
+ * [Guzzle](http://www.guzzlephp.org)
License
-------
@@ -79,3 +79,4 @@ License
Goutte is licensed under the MIT license.
[1]: https://raw.github.com/fabpot/Goutte/master/goutte.phar
+
diff --git a/autoload.php b/autoload.php
index f1a79ca..e5d8b03 100644
--- a/autoload.php
+++ b/autoload.php
@@ -9,7 +9,7 @@
$loader = new UniversalClassLoader();
$loader->registerNamespaces(array(
'Symfony' => __DIR__.'/vendor',
- 'Zend' => __DIR__.'/vendor/zend/library',
+ 'Guzzle' => __DIR__.'/vendor/Guzzle/src',
'Goutte' => __DIR__.'/src',
));
$loader->register();
diff --git a/phpunit.xml.dst b/phpunit.xml.dst
new file mode 100644
index 0000000..3bcbd37
--- /dev/null
+++ b/phpunit.xml.dst
@@ -0,0 +1,18 @@
+
+
+
+
+
+ ./tests/Goutte/Tests
+
+
+
diff --git a/src/Goutte/Client.php b/src/Goutte/Client.php
index a050c8c..4ff45ce 100644
--- a/src/Goutte/Client.php
+++ b/src/Goutte/Client.php
@@ -8,8 +8,11 @@
use Symfony\Component\BrowserKit\Request;
use Symfony\Component\BrowserKit\Response;
-use Zend\Http\Client as ZendClient;
-use Zend\Http\Response as ZendResponse;
+use Guzzle\Http\Curl\CurlException;
+use Guzzle\Http\Message\RequestInterface as GuzzleRequestInterface;
+use Guzzle\Http\Message\Response as GuzzleResponse;
+use Guzzle\Service\ClientInterface as GuzzleClientInterface;
+use Guzzle\Service\Client as GuzzleClient;
/*
* This file is part of the Goutte package.
@@ -25,68 +28,61 @@
*
* @package Goutte
* @author Fabien Potencier
+ * @author Michael Dowling
*/
class Client extends BaseClient
{
const VERSION = '0.1';
- protected $zendConfig;
protected $headers = array();
protected $auth = null;
+ protected $client;
- public function __construct(array $zendConfig = array(), array $server = array(), History $history = null, CookieJar $cookieJar = null)
+ public function setClient(GuzzleClientInterface $client)
{
- $this->zendConfig = $zendConfig;
+ $this->client = $client;
- parent::__construct($server, $history, $cookieJar);
+ return $this;
+ }
+
+ public function getClient()
+ {
+ if (!$this->client) {
+ $this->client = new GuzzleClient();
+ }
+
+ return $this->client;
}
public function setHeader($name, $value)
{
$this->headers[$name] = $value;
+
+ return $this;
}
- public function setAuth($user, $password = '', $type = ZendClient::AUTH_BASIC)
+ public function setAuth($user, $password = '', $type = GuzzleRequestInterface::AUTH_BASIC)
{
$this->auth = array(
- 'user' => $user,
+ 'user' => $user,
'password' => $password,
'type' => $type
);
- }
- protected function doRequest($request)
- {
- $client = $this->createClient($request);
-
- $response = $client->send($client->getRequest());
-
- return $this->createResponse($response);
+ return $this;
}
- protected function createClient(Request $request)
+ protected function doRequest($request)
{
- $client = $this->createZendClient();
- $client->setUri($request->getUri());
- $client->setConfig(array_merge(array(
- 'maxredirects' => 0,
- 'timeout' => 30,
- 'useragent' => $this->server['HTTP_USER_AGENT'],
- 'adapter' => 'Zend\\Http\\Client\\Adapter\\Socket',
- ), $this->zendConfig));
- $client->setMethod(strtoupper($request->getMethod()));
-
- if ($request->getContent() !== null) {
- $client->setRawBody($request->getContent());
- }
-
- if ('POST' == $request->getMethod()) {
- $client->setParameterPost($request->getParameters());
- }
- $client->setHeaders($this->headers);
+ $guzzleRequest = $this->getClient()->createRequest(
+ strtoupper($request->getMethod()),
+ $request->getUri(),
+ $this->headers,
+ $request->getParameters()
+ );
if ($this->auth !== null) {
- $client->setAuth(
+ $guzzleRequest->setAuth(
$this->auth['user'],
$this->auth['password'],
$this->auth['type']
@@ -94,46 +90,47 @@ protected function createClient(Request $request)
}
foreach ($this->getCookieJar()->allValues($request->getUri()) as $name => $value) {
- $client->addCookie($name, $value);
+ $guzzleRequest->addCookie($name, $value);
}
- $this->addFileUploadsRecursively($client, $request->getFiles());
-
- return $client;
- }
-
- /**
- * Goes recursively through the files array and adds uploads to the ZendClient
- */
- protected function addFileUploadsRecursively(ZendClient $client, array $files, $arrayName = '')
- {
- foreach ($files as $name => $info) {
- if (!empty($arrayName)) {
- $name = $arrayName . '[' . $name . ']';
+ if ($request->getMethod() == 'POST') {
+ foreach ($request->getFiles() as $name => $info) {
+ if (isset($info['tmp_name']) && '' !== $info['tmp_name']) {
+ $guzzleRequest->addPostFiles(array(
+ $name => $info['tmp_name']
+ ));
+ }
}
- if (isset($info['tmp_name']) && isset($info['name'])) {
- if ('' !== $info['tmp_name'] && '' !== $info['name']) {
- $filename = $info['name'];
-
- if (false === ($data = @file_get_contents($info['tmp_name']))) {
- throw new \RuntimeException("Unable to read file '{$filename}' for upload");
- }
+ }
- $client->setFileUpload($filename, $name, $data);
- }
- } elseif (is_array($info)) {
- $this->addFileUploadsRecursively($client, $info, $name);
+ $guzzleRequest->setHeader('User-Agent', $this->server['HTTP_USER_AGENT']);
+
+ $guzzleRequest->getCurlOptions()->merge(array(
+ CURLOPT_MAXREDIRS => 0,
+ CURLOPT_TIMEOUT => 30
+ ));
+
+ // Let BrowserKit handle redirects
+ try {
+ $response = $guzzleRequest->send();
+ } catch (CurlException $e) {
+ if (strpos($e->getMessage(), 'redirects')) {
+ $response = $e->getResponse();
+ } else {
+ throw $e;
}
}
- }
- protected function createResponse(ZendResponse $response)
- {
- return new Response($response->getBody(), $response->getStatusCode(), $response->headers()->toArray());
+ return $this->createResponse($response);
}
- protected function createZendClient()
+ protected function createResponse(GuzzleResponse $response)
{
- return new ZendClient(null, array('encodecookies' => false));
+ return new Response(
+ $response->getBody(true),
+ $response->getStatusCode(),
+ $response->getHeaders()->getAll()
+ );
}
}
+
diff --git a/src/Goutte/Compiler.php b/src/Goutte/Compiler.php
index 4993311..3108ad5 100644
--- a/src/Goutte/Compiler.php
+++ b/src/Goutte/Compiler.php
@@ -37,9 +37,7 @@ public function compile($pharFile = 'goutte.phar')
foreach ($this->getFiles() as $file)
{
$path = str_replace(__DIR__.'/', '', $file);
- $content = preg_replace("#require_once 'Zend/.*?';#", '', php_strip_whitespace($file));
-
- $phar->addFromString($path, $content);
+ $phar->addFromString($path, file_get_contents($file));
}
// Stubs
@@ -82,28 +80,7 @@ protected function getFiles()
$files = array(
'LICENSE',
'autoload.php',
- 'vendor/Symfony/Component/ClassLoader/UniversalClassLoader.php',
- 'vendor/zend/library/Zend/Registry.php',
- //'vendor/zend/library/Zend/Date.php',
- 'vendor/zend/library/Zend/Uri/Uri.php',
- 'vendor/zend/library/Zend/Validator/Validator.php',
- 'vendor/zend/library/Zend/Validator/AbstractValidator.php',
- 'vendor/zend/library/Zend/Validator/Hostname.php',
- 'vendor/zend/library/Zend/Validator/Ip.php',
- //'vendor/zend/library/Zend/Validator/Hostname/Biz.php',
- //'vendor/zend/library/Zend/Validator/Hostname/Cn.php',
- 'vendor/zend/library/Zend/Validator/Hostname/Com.php',
- 'vendor/zend/library/Zend/Validator/Hostname/Jp.php',
- 'vendor/zend/library/Zend/Stdlib/Dispatchable.php',
- 'vendor/zend/library/Zend/Stdlib/Message.php',
- 'vendor/zend/library/Zend/Stdlib/MessageDescription.php',
- 'vendor/zend/library/Zend/Stdlib/RequestDescription.php',
- 'vendor/zend/library/Zend/Stdlib/Parameters.php',
- 'vendor/zend/library/Zend/Stdlib/ParametersDescription.php',
- 'vendor/zend/library/Zend/Stdlib/ResponseDescription.php',
- 'vendor/zend/library/Zend/Loader/PluginClassLoader.php',
- 'vendor/zend/library/Zend/Loader/PluginClassLocator.php',
- 'vendor/zend/library/Zend/Loader/ShortNameLocator.php',
+ 'vendor/Symfony/Component/ClassLoader/UniversalClassLoader.php'
);
$dirs = array(
@@ -112,9 +89,7 @@ protected function getFiles()
'vendor/Symfony/Component/DomCrawler',
'vendor/Symfony/Component/CssSelector',
'vendor/Symfony/Component/Process',
- //'vendor/zend/library/Zend/Date',
- 'vendor/zend/library/Zend/Uri',
- 'vendor/zend/library/Zend/Http',
+ 'vendor/Guzzle/src/Guzzle'
);
$finder = new Finder();
@@ -123,3 +98,4 @@ protected function getFiles()
return array_merge($files, iterator_to_array($iterator));
}
}
+
diff --git a/tests/Goutte/Tests/ClientTest.php b/tests/Goutte/Tests/ClientTest.php
new file mode 100644
index 0000000..353491d
--- /dev/null
+++ b/tests/Goutte/Tests/ClientTest.php
@@ -0,0 +1,167 @@
+
+ */
+class ClientTest extends \PHPUnit_Framework_TestCase
+{
+ protected $history;
+
+ protected function getGuzzle()
+ {
+ $this->history = new HistoryPlugin();
+ $mock = new MockPlugin();
+ $mock->addResponse(new GuzzleResponse(200, null, 'Hi
'));
+ $guzzle = new GuzzleClient();
+ $guzzle->getEventManager()->attach($mock);
+ $guzzle->getEventManager()->attach($this->history);
+
+ return $guzzle;
+ }
+
+ public function testCreatesDefaultClient()
+ {
+ $client = new Client();
+ $this->assertInstanceOf('Guzzle\\Service\\Client', $client->getClient());
+ }
+
+ public function testUsesCustomClient()
+ {
+ $guzzle = new GuzzleClient();
+ $client = new Client();
+ $this->assertSame($client, $client->setClient($guzzle));
+ $this->assertSame($guzzle, $client->getClient());
+ }
+
+ public function testUsesCustomHeaders()
+ {
+ $guzzle = $this->getGuzzle();
+ $client = new Client();
+ $client->setClient($guzzle);
+ $client->setHeader('X-Test', 'test');
+ $crawler = $client->request('GET', 'http://test.com/');
+ $this->assertEquals('test', $this->history->getLastRequest()->getHeader('X-Test'));
+ }
+
+ public function testUsesAuth()
+ {
+ $guzzle = $this->getGuzzle();
+ $client = new Client();
+ $client->setClient($guzzle);
+ $client->setAuth('me', '**');
+ $crawler = $client->request('GET', 'http://test.com/');
+ $request = $this->history->getLastRequest();
+ $this->assertEquals('me', $request->getUsername());
+ $this->assertEquals('**', $request->getPassword());
+ }
+
+ public function testUsesCookies()
+ {
+ $guzzle = $this->getGuzzle();
+ $client = new Client();
+ $client->setClient($guzzle);
+ $client->getCookieJar()->set(new Cookie('test', '123'));
+ $crawler = $client->request('GET', 'http://test.com/');
+ $request = $this->history->getLastRequest();
+ $this->assertEquals('123', $request->getCookie('test'));
+ }
+
+ public function testUsesPostFiles()
+ {
+ $guzzle = $this->getGuzzle();
+ $client = new Client();
+ $client->setClient($guzzle);
+ $files = array(
+ 'test' => array(
+ 'name' => 'test.txt',
+ 'tmp_name' => __FILE__
+ )
+ );
+
+ $crawler = $client->request('POST', 'http://test.com/', array(), $files);
+ $request = $this->history->getLastRequest();
+
+ $this->assertEquals(array(
+ 'test' => __FILE__
+ ), $request->getPostFiles());
+ }
+
+ public function testUsesCurlOptions()
+ {
+ $guzzle = $this->getGuzzle();
+ $client = new Client();
+ $client->setClient($guzzle);
+ $crawler = $client->request('GET', 'http://test.com/');
+ $request = $this->history->getLastRequest();
+ $this->assertEquals(0, $request->getCurlOptions()->get(CURLOPT_MAXREDIRS));
+ $this->assertEquals(30, $request->getCurlOptions()->get(CURLOPT_TIMEOUT));
+ }
+
+ public function testCreatesResponse()
+ {
+ $guzzle = $this->getGuzzle();
+ $client = new Client();
+ $client->setClient($guzzle);
+ $crawler = $client->request('GET', 'http://test.com/');
+ $this->assertEquals('Hi', $crawler->filter('p')->text());
+ }
+
+ public function testHandlesRedirectsCorrectly()
+ {
+ $guzzle = $this->getGuzzle();
+ $plugins = $guzzle->getEventManager()->getAttached('Guzzle\\Service\\Plugin\\MockPlugin');
+ $mock = $plugins[0];
+
+ $mock->clearQueue();
+ $mock->addResponse(new GuzzleResponse(301, array(
+ 'Location' => 'http://www.test.com/'
+ )));
+ $mock->addResponse(new GuzzleResponse(200, null, 'Test
'));
+
+ $client = new Client();
+ $client->setClient($guzzle);
+
+ $crawler = $client->request('GET', 'http://test.com/');
+ $this->assertEquals('Test', $crawler->filter('p')->text());
+
+ // Ensure that two requests were sent
+ $this->assertEquals(2, count($this->history));
+ }
+}
\ No newline at end of file
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 0000000..ea4e660
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,6 @@
+