├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── example ├── make_a_delete_request ├── make_a_get_request ├── make_a_patch_request ├── make_a_post_request └── make_a_put_request ├── phpunit.xml.dist ├── source ├── Builder │ ├── Builder.php │ └── BuilderFactory.php ├── Dispatcher │ ├── Dispatcher.php │ ├── DispatcherInterface.php │ └── LoggingDispatcher.php ├── FactoryInterface.php ├── HeaderLine │ ├── AbstractContentType.php │ ├── AbstractHeaderLine.php │ ├── AcceptEncoding.php │ ├── AcceptLanguage.php │ ├── ContentTypeIsUtf8Form.php │ ├── ContentTypeIsUtf8Html.php │ ├── ContentTypeIsUtf8Json.php │ ├── Custom.php │ └── HeaderLineInterface.php ├── Option │ ├── AbstractAuthentication.php │ ├── AbstractSetOptionArrayValue.php │ ├── AbstractSetOptionClosureValue.php │ ├── AbstractSetOptionIntValue.php │ ├── AbstractSetOptionMixedValue.php │ ├── AbstractSetOptionStreamValue.php │ ├── AbstractSetOptionStringValue.php │ ├── AbstractSetOptionToFalse.php │ ├── AbstractSetOptionToTrue.php │ ├── Authentication │ │ ├── EnableUnrestrictedAuth.php │ │ ├── SetBasicAuthentication.php │ │ ├── SetKeyPassword.php │ │ └── SetUsernameAndPassword.php │ ├── Authorization │ │ └── EnableNetrc.php │ ├── Behaviour │ │ ├── DisableBody.php │ │ ├── DisableProgress.php │ │ ├── DisableSignal.php │ │ ├── EnableAutoReferer.php │ │ ├── EnableFailOnError.php │ │ ├── EnableFollowAllocation.php │ │ ├── EnableForbidReuse.php │ │ ├── EnableFreshConnect.php │ │ ├── EnableMute.php │ │ ├── EnableVerbose.php │ │ ├── SetConnectTimeOutInMilliSeconds.php │ │ ├── SetConnectTimeOutInSeconds.php │ │ ├── SetDnsCacheTimeout.php │ │ ├── SetHttp200Aliases.php │ │ ├── SetLowSpeedLimit.php │ │ ├── SetMaxConnects.php │ │ ├── SetMaxRedirs.php │ │ ├── SetRange.php │ │ ├── SetRedirProtocols.php │ │ ├── SetResumeFrom.php │ │ ├── SetStderr.php │ │ ├── SetTcpNoDelay.php │ │ ├── SetTimeCondition.php │ │ ├── SetTimeOutInMilliSeconds.php │ │ └── SetTimeOutInSeconds.php │ ├── Callback │ │ ├── SetCallbackForPassWordFunction.php │ │ ├── SetCallbackForProgressFunction.php │ │ ├── SetCallbackForReadFunction.php │ │ └── SetCallbackForWriteFunction.php │ ├── Cookie │ │ ├── EnableCookieSession.php │ │ ├── SetCookie.php │ │ ├── SetCookieFile.php │ │ └── SetCookieJar.php │ ├── Ftp │ │ ├── EnableFtpAppend.php │ │ ├── EnableFtpAscii.php │ │ ├── EnableFtpCreateMissingDirs.php │ │ ├── EnableFtpListOnly.php │ │ ├── EnableFtpUseEprt.php │ │ ├── EnableFtpUseEpsv.php │ │ ├── SetFtpPort.php │ │ ├── SetFtpSslAuth.php │ │ ├── SetPostQuote.php │ │ └── SetQuote.php │ ├── OptionInterface.php │ ├── Security │ │ ├── DisableSslVerifyHost.php │ │ ├── DisableSslVerifyPeer.php │ │ ├── EnableCertInfo.php │ │ ├── SetCaInfo.php │ │ ├── SetCaPath.php │ │ ├── SetSslCert.php │ │ ├── SetSslCertPasswd.php │ │ ├── SetSslCertType.php │ │ ├── SetSslCipherList.php │ │ ├── SetSslEngine.php │ │ ├── SetSslEngineDefault.php │ │ ├── SetSslKey.php │ │ ├── SetSslKeyPasswd.php │ │ ├── SetSslKeyType.php │ │ └── SetSslVersion.php │ ├── SetOption.php │ └── Transfer │ │ ├── DisableDnsUseGlobalCache.php │ │ ├── EnableBinaryTransfer.php │ │ ├── EnableCrlf.php │ │ ├── EnableFileTime.php │ │ ├── EnableHeader.php │ │ ├── EnableHttpProxyTunnel.php │ │ ├── EnableSafeUpload.php │ │ ├── EnableTransferText.php │ │ ├── EnableUpload.php │ │ ├── SetBufferSize.php │ │ ├── SetEgdSocket.php │ │ ├── SetEncoding.php │ │ ├── SetFile.php │ │ ├── SetHttpVersion.php │ │ ├── SetInFile.php │ │ ├── SetInFileSize.php │ │ ├── SetInterface.php │ │ ├── SetPort.php │ │ ├── SetProxy.php │ │ ├── SetProxyAuth.php │ │ ├── SetProxyPort.php │ │ ├── SetProxyType.php │ │ ├── SetProxyUserPwd.php │ │ ├── SetRandomFile.php │ │ ├── SetReferer.php │ │ ├── SetTimeValue.php │ │ ├── SetUserAgent.php │ │ └── SetWriteHeader.php ├── Request │ ├── Request.php │ └── RequestFactory.php ├── Response │ └── Response.php └── ResponseBehaviour │ ├── ConvertJsonToArrayBehaviour.php │ ├── ResponseBehaviourInterface.php │ └── ThrowRuntimeExceptionIfStatusCodeIsAboveTheLimitBehaviour.php └── test ├── AbstractTestCase.php ├── Builder └── BuilderTest.php ├── Request └── RequestTest.php ├── Response └── ResponseTest.php ├── ResponseBehaviour ├── ConvertJsonToArrayBehaviourTest.php └── ThrowRuntimeExceptionIfStatusCodeIsAboveTheLimitBehaviourTest.php └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | composer.lock 4 | vendor 5 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | paths: 3 | - source/* 4 | excluded_paths: 5 | - vendor/* 6 | 7 | checks: 8 | php: 9 | code_rating: true 10 | duplication: true 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 5.6 4 | - 7.0 5 | - 7.1 6 | - 7.2 7 | before_script: 8 | - composer self-update 9 | - composer install 10 | - phpenv rehash 11 | script: 12 | - vendor/bin/phpunit -v --colors --coverage-text 13 | notifications: 14 | email: 15 | - artodeto@bazzline.net 16 | sudo: false 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Open] 9 | 10 | ### To Add 11 | 12 | * to discuss 13 | * add [dispatcher](https://github.com/jyggen/curl/blob/master/src/Dispatcher.php) or HandlerGenerator/HandlerFactory 14 | * https://secure.php.net/manual/en/function.curl-init.php 15 | * https://secure.php.net/manual/en/function.curl-multi-init.php 16 | * add RequestModifier 17 | * e.g. for adding the JsonModifier which converts the data into a json, adds the fitting ContentType etc. 18 | * add support for parallel request execution like in the [stil/curl](https://github.com/stil/curl-easy) library 19 | * replace current dispatcher and logging dispatcher strategy with an event driven approach (currently only needed for logging)? 20 | * create Request Data Domain Object 21 | * create Request Options Domain Object 22 | 23 | ### To Change 24 | 25 | ## [Unreleased] 26 | 27 | ### Added 28 | 29 | * added php 7.2 to the travis test 30 | 31 | ### Changed 32 | 33 | * updated composer.json file and restricted php version to 5.6 as minimum 34 | 35 | ## [1.0.0](https://github.com/bazzline/php_component_curl/tree/1.0.0) - released at 2017-04-23 36 | 37 | ### Added 38 | 39 | * support for php version 7.1 40 | 41 | ### Changed 42 | 43 | * dropped support for php version below 5.6 44 | * moved release history from the README.md into this dedicated CHANGELOG.md 45 | * replaced "array()" notation with "[]" 46 | 47 | ## [0.15.1](https://github.com/bazzline/php_component_curl/tree/0.15.1) - released at 2017-03-29 48 | 49 | ### Changed 50 | 51 | * moved phpunit dependency to 5.7 52 | 53 | ## [0.15.0](https://github.com/bazzline/php_component_curl/tree/0.15.0) - released at 2016-10-27 54 | 55 | ### Changed 56 | 57 | * support to send data when calling http method DELETE 58 | 59 | ## [0.14.4](https://github.com/bazzline/php_component_curl/tree/0.14.4) - released at 2016-09-20 60 | 61 | ### Added 62 | 63 | * [AcceptLanguate](https://github.com/bazzline/php_component_curl/blob/0.14.4/source/HeaderLine/AcceptLanguage.php) header line 64 | 65 | ## [0.14.3](https://github.com/bazzline/php_component_curl/tree/0.14.3) - released at 2016-09-19 66 | 67 | ### Added 68 | 69 | * [AcceptEncoding](https://github.com/bazzline/php_component_curl/blob/0.14.3/source/HeaderLine/AcceptEncoding.php) header line 70 | * [Custom](https://github.com/bazzline/php_component_curl/blob/0.14.3/source/HeaderLine/Custom.php) header line 71 | * started more examples section 72 | 73 | ### Changed 74 | 75 | * updated phpunit to 5.5.* 76 | 77 | ## [0.14.2](https://github.com/bazzline/php_component_curl/tree/0.14.2) - released at 2016-05-30 78 | 79 | ### Changed 80 | 81 | * relaxed dependency to mockery 82 | 83 | ## [0.14.1](https://github.com/bazzline/php_component_curl/tree/0.14.1) - released at 2016-03-11 84 | 85 | ### Changed 86 | 87 | * removed *.php* for examples and made them executable 88 | * updated dependencies 89 | 90 | ## [0.14.0](https://github.com/bazzline/php_component_curl/tree/0.14.0) - released at 2016-02-26 91 | 92 | ### Changed 93 | 94 | * removed default header "application/x-www-form-urlencoded; charset=UTF-8" and added new *ContentTypeUtf8Html* 95 | 96 | ## [0.13.0](https://github.com/bazzline/php_component_curl/tree/0.13.0) - released at 2016-02-17 97 | 98 | ### Changed 99 | 100 | * fixed "ConverteJsonToArayBehaviour" by merging [pull request 3](https://github.com/bazzline/php_component_curl/pull/3) 101 | 102 | ## [0.12.0](https://github.com/bazzline/php_component_curl/tree/0.12.0) - released at 2016-02-08 103 | 104 | ### Added 105 | 106 | * *curl_close($handler)* in default *Dispatcher* 107 | 108 | ### Changed 109 | 110 | * fixed bug in *BuilderFactory::createRequestFromFactory()* 111 | * fixed bug in *RequestFactory::create()* 112 | 113 | ## [0.11.0](https://github.com/bazzline/php_component_curl/tree/0.11.0) - released at 2016-02-08 114 | 115 | * fixed major bug in *createRequestFromFactory* to *BuilderFactory* 116 | * refactored internals of Builder::andFetchTheResponse() 117 | * refactored internals of Request::execute() 118 | * refactored internals of RequestFactory::create() 119 | 120 | ## [0.10.0](https://github.com/bazzline/php_component_curl/tree/0.10.0) - released at 08.02.2016 121 | 122 | ### Added 123 | 124 | * public *overwriteRequestFactory()* to *BuilderFactory* 125 | 126 | ### Changed 127 | 128 | * fixed broken links in the readme 129 | * moved to *psr-4* autoloading 130 | * removed dedicated integration test for php 5.3.3 131 | 132 | ## [0.9.1](https://github.com/bazzline/php_component_curl/tree/0.9.1) - released at 2016-01-14 133 | 134 | ### Changed 135 | 136 | * updated dependencies 137 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Curl Wrapper Component for PHP 2 | 3 | This project aims to deliver an easy to use and free as in freedom object oriented php curl command component. 4 | 5 | The build status of the current master branch is tracked by Travis CI: 6 | [![Build Status](https://travis-ci.org/bazzline/php_component_curl.png?branch=master)](http://travis-ci.org/bazzline/php_component_curl) 7 | [![Latest stable](https://img.shields.io/packagist/v/net_bazzline/php_component_curl.svg)](https://packagist.org/packages/net_bazzline/php_component_curl) 8 | 9 | The scrutinizer status is: 10 | [![code quality](https://scrutinizer-ci.com/g/bazzline/php_component_curl/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/bazzline/php_component_curl/) 11 | 12 | The versioneye status is: 13 | [![Dependency Status](https://www.versioneye.com/user/projects/553941560b24225ef6000002/badge.svg?style=flat)](https://www.versioneye.com/user/projects/553941560b24225ef6000002) 14 | 15 | Take a look on [openhub.net](https://www.openhub.net/p/php_component_curl). 16 | 17 | The current change log can be found [here](https://github.com/bazzline/php_component_curl/blob/master/CHANGELOG.md). 18 | 19 | This component is not developed to replace [guzzle](http://docs.guzzlephp.org/en/latest/). 20 | 21 | # Example 22 | 23 | ## By Using The Builder 24 | 25 | ```php 26 | //it is always good to ship the component with a factory to easy up usage 27 | use Net\Bazzline\Component\Curl\BuilderFactory; 28 | use Net\Bazzline\Component\Curl\Option\Timeout; 29 | 30 | $factory = new BuilderFactory(); 31 | $builder = $factory->create(); 32 | $url = 'http://www.foo.bar'; 33 | $timeout = new Timeout(10); //set the timeout to 10 seconds 34 | 35 | /** 36 | * you can also use: 37 | * //assuming that $dispatcher is an instance of DispatcherInterface 38 | * //assuming that $requestFactory is an instance of RequestFactory 39 | * $builder->overwriteDispatcher($dispatcher); 40 | * $builder->overwriteRequestFactory($requestFactory); 41 | */ 42 | 43 | $response = $builder->usePost() 44 | ->onTheUrl($url) 45 | ->withTheData($data) 46 | ->withTheParameters(array('descendingOrderBy' => 'id')) 47 | ->withTheOption($timeout) 48 | ->andFetchTheResponse(); 49 | 50 | /** 51 | * you can also use: 52 | * $builder->withTheHeaderLine($headLine) //add the headline you want 53 | * $builder->withResponseModifier($modifier) //add the response modifier you want 54 | */ 55 | 56 | echo 'content: ' . $response->content() . PHP_EOL; 57 | echo 'content type: ' . $response->contentType() . PHP_EOL; 58 | echo 'error:' . $response->error() . PHP_EOL; 59 | echo 'error code:' . $response->errorCode() . PHP_EOL; 60 | echo 'head lines: ' . var_export($response->headerLines(), true) . PHP_EOL; 61 | echo 'status code: ' . $response->statusCode() . PHP_EOL; 62 | ``` 63 | 64 | ## By Using The Request 65 | 66 | ```php 67 | //it is always good to ship the component with a factory to easy up usage 68 | $factory = new Net\Bazzline\Component\Curl\RequestFactory(); 69 | $request = $factory->create(); 70 | $url = 'http://www.foo.bar'; 71 | 72 | $response = $request->get($url); 73 | 74 | echo 'content: ' . $response->content() . PHP_EOL; 75 | echo 'content type: ' . $response->contentType() . PHP_EOL; 76 | echo 'error:' . $response->error() . PHP_EOL; 77 | echo 'error code:' . $response->errorCode() . PHP_EOL; 78 | echo 'head lines: ' . var_export($response->headerLines(), true) . PHP_EOL; 79 | echo 'status code: ' . $response->statusCode() . PHP_EOL; 80 | ``` 81 | 82 | ## Executable Examples 83 | 84 | * [Delete Request](https://github.com/bazzline/php_component_curl/blob/master/example/make_a_delete_request) 85 | * [Get Request](https://github.com/bazzline/php_component_curl/blob/master/example/make_a_delete_request) 86 | * [Patch Request](https://github.com/bazzline/php_component_curl/blob/master/example/make_a_patch_request) 87 | * [Post Request](https://github.com/bazzline/php_component_curl/blob/master/example/make_a_post_request) 88 | * [Put Request](https://github.com/bazzline/php_component_curl/blob/master/example/make_a_put_request) 89 | 90 | ## More Examples 91 | 92 | ### Post Request With Basic Authentication 93 | 94 | ``` 95 | //begin of runtime environments 96 | $factory = new BuilderFactory(); 97 | $builder = $factory->create(); 98 | $data = array( 99 | 'there' => 'is', 100 | 'no' => 'foo', 101 | 'without' => 'a bar' 102 | ); 103 | $password = ''; 104 | $username = 'foo@bar.ru'; 105 | $url = 'https://foo.bar.ru/api/my/rest/endpoint/v1'; 106 | //end of runtime environments 107 | 108 | //begin building the request 109 | $builder->asJson(); 110 | $builder->onTheUrl($url); 111 | $builder->withTheData($data); 112 | $builder->withTheOption(new SetBasicAuthentication()); 113 | $builder->withTheOption(new SetUsernameAndPassword($username, $password)); 114 | $builder->usePost(); 115 | //end building the request 116 | 117 | $request = $builder->andFetchTheResponse(); 118 | echo PHP_EOL . 'dumping the request' . PHP_EOL; 119 | var_dump($request); 120 | ``` 121 | 122 | # Terms 123 | 124 | * [Dispatcher](https://github.com/bazzline/php_component_curl/blob/master/source/Net/Bazzline/Component/Curl/Dispatcher/Dispatcher.php) 125 | * doing the curl request 126 | * if you want to use pure curl, use this class 127 | * [Request](https://github.com/bazzline/php_component_curl/blob/master/source/Net/Bazzline/Component/Curl/Request/Request.php) 128 | * object oriented approach reflecting the request 129 | * [HeadLine](https://github.com/bazzline/php_component_curl/blob/master/source/Net/Bazzline/Component/Curl/HeadLine/HeadLineInterface.php) 130 | * object oriented [http headers](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) ([list](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields) of available headers), start a pull request if you need more 131 | * [Options](https://github.com/bazzline/php_component_curl/blob/master/source/Net/Bazzline/Component/Curl/Option/OptionInterface.php) 132 | * object oriented curl options, start a pull request if you need more 133 | * Parameters 134 | * all the parameters you want to add to your url - they are urlencoded automatically 135 | * Url 136 | * the url of your endpoint 137 | * [Response](https://github.com/bazzline/php_component_curl/blob/master/source/Net/Bazzline/Component/Curl/Response/Response.php) 138 | * object oriented approach reflecting the response 139 | * [ResponseBehaviour](https://github.com/bazzline/php_component_curl/blob/master/source/Net/Bazzline/Component/Curl/ResponseBehaviour/ResponseBehaviourInterface.php) 140 | * interface to interact with the response 141 | * either modify the response (by creating a new one) 142 | * change the flow by throwing an exception if the response does not fits your needs (as example) 143 | * [Builder](https://github.com/bazzline/php_component_curl/blob/master/source/Net/Bazzline/Component/Curl/Builder/Builder.php) 144 | * provides a fluent interface to easy up using curl 145 | * it takes care of all :-) 146 | 147 | # Not Available Curl Options 148 | 149 | In general, the php version is limiting the available [curl options](http://php.net/manual/en/curl.constants.php). 150 | Furthermore, some options are not implemented because of their hardcoded usage in the *Response* or the *Dispatcher* (you can set it but they would be overwritten). 151 | 152 | This options are: 153 | 154 | * used in the *Request* 155 | * CURLOPT_CUSTOMREQUEST 156 | * CURLOPT_HTTPHEADER 157 | * CURLOPT_POSTFIELDS 158 | * used in the *Dispatcher* 159 | * CURLINFO_HEADER_OUT 160 | * CURLOPT_HEADERFUNCTION 161 | * CURLOPT_RETURNTRANSFER 162 | 163 | If you want to change this, you either have to extend the existing *Request* or *Dispatcher* object. 164 | 165 | # Install 166 | 167 | ## By Hand 168 | 169 | ``` 170 | mkdir -p vendor/net_bazzline/php_component_curl 171 | cd vendor/net_bazzline/php_component_curl 172 | git clone https://github.com/bazzline/php_component_curl . 173 | ``` 174 | 175 | ## With [Packagist](https://packagist.org/packages/net_bazzline/php_component_curl) 176 | 177 | ``` 178 | composer require net_bazzline/php_component_curl:dev-master 179 | ``` 180 | 181 | # Links 182 | 183 | * http://resttesttest.com/ 184 | 185 | ## Other Components Available 186 | 187 | * https://github.com/php-mod/curl 188 | * https://github.com/anlutro/php-curl 189 | * https://github.com/hamstar/curl 190 | * https://github.com/jyggen/curl 191 | * https://github.com/ixudra/Curl 192 | * https://github.com/brodkinca/BCA-PHP-CURL 193 | * https://github.com/miliqi/laravel-curl 194 | * https://github.com/andrefigueira/Lib-Curl 195 | 196 | # Final Words 197 | 198 | Star it if you like it :-). Add issues if you need it. Pull patches if you enjoy it. Write a blog entry if you use it. [Donate something](https://gratipay.com/~stevleibelt) if you love it :-]. 199 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "net_bazzline/php_component_curl", 3 | "description": "free as in freedom simple object oriented curl request and response component (YACC - yet another curl component)", 4 | "keywords": [ 5 | "bazzline", 6 | "psr", 7 | "psr-4", 8 | "curl", 9 | "oop", 10 | "yet another", 11 | "request", 12 | "response", 13 | "dispatcher", 14 | "lgpl", 15 | "free as in freedom", 16 | "yacc", 17 | "php56", 18 | "php7", 19 | "changelog" 20 | ], 21 | "type": "library", 22 | "minimum-stability": "dev", 23 | "require": { 24 | "net_bazzline/php_component_toolbox": "1.9.*", 25 | "php": ">=5.6" 26 | }, 27 | "require-dev": { 28 | "mockery/mockery": "0.9.*", 29 | "phpunit/phpunit": "^5.7" 30 | }, 31 | "license": "LGPL-3.0", 32 | "authors": [ 33 | { 34 | "name": "Stev Leibelt", 35 | "email": "artodeto@bazzline.net", 36 | "homepage": "https://artodeto.bazzline.net", 37 | "role": "Developer" 38 | } 39 | ], 40 | "autoload": { 41 | "psr-4": { 42 | "Net\\Bazzline\\Component\\Curl\\": "source/" 43 | } 44 | }, 45 | "autoload-dev": { 46 | "psr-4": { 47 | "Test\\Net\\Bazzline\\Component\\Curl\\": "test/" 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /example/make_a_delete_request: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 5 | * @since 2015-12-10 6 | */ 7 | 8 | require_once __DIR__ . '/../vendor/autoload.php'; 9 | 10 | use Net\Bazzline\Component\Curl\Builder\BuilderFactory; 11 | use Net\Bazzline\Component\Curl\Dispatcher\LoggingDispatcher; 12 | 13 | $factory = new BuilderFactory(); 14 | $factory->overwriteDispatcher(new LoggingDispatcher()); 15 | $builder = $factory->create(); 16 | $url = ($argc > 1) ? $argv[1] : 'https://httpbin.org/get'; 17 | 18 | $response = $builder->onTheUrl($url) 19 | ->useDelete() 20 | ->andFetchTheResponse(); 21 | 22 | echo 'content: ' . $response->content() . PHP_EOL; 23 | echo 'content type: ' . $response->contentType() . PHP_EOL; 24 | echo 'head lines: ' . var_export($response->headerLines(), true) . PHP_EOL; 25 | echo 'error:' . $response->error() . PHP_EOL; 26 | echo 'error code:' . $response->errorCode() . PHP_EOL; 27 | echo 'status code: ' . $response->statusCode() . PHP_EOL; 28 | -------------------------------------------------------------------------------- /example/make_a_get_request: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 5 | * @since 2015-12-10 6 | */ 7 | 8 | require_once __DIR__ . '/../vendor/autoload.php'; 9 | 10 | use Net\Bazzline\Component\Curl\Builder\BuilderFactory; 11 | use Net\Bazzline\Component\Curl\Dispatcher\LoggingDispatcher; 12 | 13 | $factory = new BuilderFactory(); 14 | $factory->overwriteDispatcher(new LoggingDispatcher()); 15 | $builder = $factory->create(); 16 | $url = ($argc > 1) ? $argv[1] : 'https://httpbin.org/get'; 17 | 18 | $response = $builder->onTheUrl($url) 19 | ->useGet() 20 | ->andFetchTheResponse(); 21 | 22 | echo 'content: ' . $response->content() . PHP_EOL; 23 | echo 'content type: ' . $response->contentType() . PHP_EOL; 24 | echo 'head lines: ' . var_export($response->headerLines(), true) . PHP_EOL; 25 | echo 'error:' . $response->error() . PHP_EOL; 26 | echo 'error code:' . $response->errorCode() . PHP_EOL; 27 | echo 'status code: ' . $response->statusCode() . PHP_EOL; 28 | -------------------------------------------------------------------------------- /example/make_a_patch_request: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 5 | * @since 2015-12-10 6 | */ 7 | 8 | require_once __DIR__ . '/../vendor/autoload.php'; 9 | 10 | use Net\Bazzline\Component\Curl\Builder\BuilderFactory; 11 | use Net\Bazzline\Component\Curl\Dispatcher\LoggingDispatcher; 12 | 13 | $factory = new BuilderFactory(); 14 | $factory->overwriteDispatcher(new LoggingDispatcher()); 15 | $builder = $factory->create(); 16 | $url = ($argc > 1) ? $argv[1] : 'https://httpbin.org/get'; 17 | 18 | $response = $builder->onTheUrl($url) 19 | ->usePatch() 20 | ->andFetchTheResponse(); 21 | 22 | echo 'content: ' . $response->content() . PHP_EOL; 23 | echo 'content type: ' . $response->contentType() . PHP_EOL; 24 | echo 'head lines: ' . var_export($response->headerLines(), true) . PHP_EOL; 25 | echo 'error:' . $response->error() . PHP_EOL; 26 | echo 'error code:' . $response->errorCode() . PHP_EOL; 27 | echo 'status code: ' . $response->statusCode() . PHP_EOL; 28 | -------------------------------------------------------------------------------- /example/make_a_post_request: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 5 | * @since 2015-12-10 6 | */ 7 | 8 | require_once __DIR__ . '/../vendor/autoload.php'; 9 | 10 | use Net\Bazzline\Component\Curl\Builder\BuilderFactory; 11 | use Net\Bazzline\Component\Curl\Dispatcher\LoggingDispatcher; 12 | 13 | $factory = new BuilderFactory(); 14 | $factory->overwriteDispatcher(new LoggingDispatcher()); 15 | $builder = $factory->create(); 16 | $url = ($argc > 1) ? $argv[1] : 'https://httpbin.org/get'; 17 | 18 | $response = $builder->onTheUrl($url) 19 | ->usePost() 20 | ->andFetchTheResponse(); 21 | 22 | echo 'content: ' . $response->content() . PHP_EOL; 23 | echo 'content type: ' . $response->contentType() . PHP_EOL; 24 | echo 'head lines: ' . var_export($response->headerLines(), true) . PHP_EOL; 25 | echo 'error:' . $response->error() . PHP_EOL; 26 | echo 'error code:' . $response->errorCode() . PHP_EOL; 27 | echo 'status code: ' . $response->statusCode() . PHP_EOL; 28 | -------------------------------------------------------------------------------- /example/make_a_put_request: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 5 | * @since 2015-12-10 6 | */ 7 | 8 | require_once __DIR__ . '/../vendor/autoload.php'; 9 | 10 | use Net\Bazzline\Component\Curl\Builder\BuilderFactory; 11 | use Net\Bazzline\Component\Curl\Dispatcher\LoggingDispatcher; 12 | 13 | $factory = new BuilderFactory(); 14 | $factory->overwriteDispatcher(new LoggingDispatcher()); 15 | $builder = $factory->create(); 16 | $url = ($argc > 1) ? $argv[1] : 'https://httpbin.org/get'; 17 | 18 | $response = $builder->onTheUrl($url) 19 | ->usePut() 20 | ->andFetchTheResponse(); 21 | 22 | echo 'content: ' . $response->content() . PHP_EOL; 23 | echo 'content type: ' . $response->contentType() . PHP_EOL; 24 | echo 'head lines: ' . var_export($response->headerLines(), true) . PHP_EOL; 25 | echo 'error:' . $response->error() . PHP_EOL; 26 | echo 'error code:' . $response->errorCode() . PHP_EOL; 27 | echo 'status code: ' . $response->statusCode() . PHP_EOL; 28 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | test/ 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /source/Builder/Builder.php: -------------------------------------------------------------------------------- 1 | 4 | * @since 2015-12-08 5 | */ 6 | namespace Net\Bazzline\Component\Curl\Builder; 7 | 8 | use Exception; 9 | use Net\Bazzline\Component\Curl\HeaderLine\HeaderLineInterface; 10 | use Net\Bazzline\Component\Curl\HeaderLine\ContentTypeIsUtf8Json; 11 | use Net\Bazzline\Component\Curl\Option\OptionInterface; 12 | use Net\Bazzline\Component\Curl\Request\Request; 13 | use Net\Bazzline\Component\Curl\Response\Response; 14 | use Net\Bazzline\Component\Curl\ResponseBehaviour\ConvertJsonToArrayBehaviour; 15 | use Net\Bazzline\Component\Curl\ResponseBehaviour\ResponseBehaviourInterface; 16 | use Net\Bazzline\Component\Toolbox\HashMap\Merge; 17 | use RuntimeException; 18 | 19 | class Builder 20 | { 21 | const METHOD_DELETE = 0; 22 | const METHOD_GET = 1; 23 | const METHOD_PATCH = 2; 24 | const METHOD_POST = 3; 25 | const METHOD_PUT = 4; 26 | 27 | /** @var boolean */ 28 | private $asJson; 29 | 30 | /** @var null|string|array */ 31 | private $data; 32 | 33 | /** @var array|ResponseBehaviourInterface[] */ 34 | private $defaultResponseBehaviours; 35 | 36 | /** @var Merge */ 37 | private $merge; 38 | 39 | /** @var int */ 40 | private $method; 41 | 42 | /** @var array */ 43 | private $parameters; 44 | 45 | /** @var Request */ 46 | private $request; 47 | 48 | /** @var array|ResponseBehaviourInterface[] */ 49 | private $responseBehaviours; 50 | 51 | /** @var string */ 52 | private $url; 53 | 54 | /** 55 | * @param Request $request 56 | * @param Merge $merge 57 | * @param array|ResponseBehaviourInterface[] $defaultResponseBehaviours 58 | */ 59 | public function __construct(Request $request, Merge $merge, array $defaultResponseBehaviours = []) 60 | { 61 | $this->defaultResponseBehaviours = $defaultResponseBehaviours; 62 | $this->merge = $merge; 63 | $this->request = $request; 64 | $this->reset(); 65 | } 66 | 67 | /** 68 | * @return Response 69 | * @throws Exception|RuntimeException 70 | */ 71 | public function andFetchTheResponse() 72 | { 73 | //begin of dependencies 74 | $asJson = $this->asJson; 75 | $merge = $this->merge; 76 | $data = $this->data; 77 | $method = $this->method; 78 | $parameters = $this->parameters; 79 | $request = $this->request; 80 | $url = $this->url; 81 | //end of dependencies 82 | 83 | //begin of business logic 84 | /** @var ResponseBehaviourInterface[] $behaviours */ 85 | $behaviours = $merge($this->responseBehaviours, $this->defaultResponseBehaviours); 86 | $data = $this->convertToJsonIfNeeded($data, $asJson); 87 | $response = $this->fetchResponseFromRequestOrThrowRuntimeException( 88 | $method, 89 | $request, 90 | $url, 91 | $parameters, 92 | $data 93 | ); 94 | $response = $this->applyBehaviours($behaviours, $response); 95 | //end of business logic 96 | 97 | return $response; 98 | } 99 | 100 | /** 101 | * @return $this 102 | */ 103 | public function asJson() 104 | { 105 | $this->asJson = true; 106 | $this->request->addHeaderLine(new ContentTypeIsUtf8Json()); 107 | $this->responseBehaviours[] = new ConvertJsonToArrayBehaviour(); 108 | 109 | return $this; 110 | } 111 | 112 | /** 113 | * @param bool $alsoTheDefaults 114 | * @return $this 115 | */ 116 | public function reset($alsoTheDefaults = false) 117 | { 118 | $this->asJson = false; 119 | $this->data = null; 120 | $this->parameters = []; 121 | $this->request->reset($alsoTheDefaults); 122 | $this->responseBehaviours = []; 123 | $this->url = null; 124 | 125 | return $this; 126 | } 127 | 128 | /** 129 | * @param string $url 130 | * @return $this 131 | */ 132 | public function onTheUrl($url) 133 | { 134 | $this->url = $url; 135 | 136 | return $this; 137 | } 138 | 139 | /** 140 | * @param null|string|array $data 141 | * @return $this 142 | */ 143 | public function withTheData($data) 144 | { 145 | $this->data = $data; 146 | 147 | return $this; 148 | } 149 | 150 | /** 151 | * @param HeaderLineInterface $line 152 | * @return $this 153 | */ 154 | public function withTheHeaderLine(HeaderLineInterface $line) 155 | { 156 | $this->request->addHeaderLine($line); 157 | 158 | return $this; 159 | } 160 | 161 | /** 162 | * @param OptionInterface $option 163 | * @return $this 164 | */ 165 | public function withTheOption(OptionInterface $option) 166 | { 167 | $this->request->addOption($option); 168 | 169 | return $this; 170 | } 171 | 172 | /** 173 | * @param array $parameters 174 | * @return $this 175 | */ 176 | public function withTheParameters(array $parameters) 177 | { 178 | $this->parameters = $parameters; 179 | 180 | return $this; 181 | } 182 | 183 | /** 184 | * @param string $line 185 | * @return $this 186 | */ 187 | public function withTheRawHeaderLine($line) 188 | { 189 | $this->request->addRawHeaderLine($line); 190 | 191 | return $this; 192 | } 193 | 194 | /** 195 | * @param string $key 196 | * @param string $value 197 | * @return $this 198 | */ 199 | public function withTheRawOption($key, $value) 200 | { 201 | $this->request->addRawOption($key, $value); 202 | 203 | return $this; 204 | } 205 | 206 | /** 207 | * @param ResponseBehaviourInterface $behaviour 208 | * @return $this 209 | */ 210 | public function withTheResponseBehaviour(ResponseBehaviourInterface $behaviour) 211 | { 212 | $this->responseBehaviours[] = $behaviour; 213 | 214 | return $this; 215 | } 216 | 217 | //@todo better naming? 218 | // callDelete 219 | /** 220 | * @return $this 221 | */ 222 | public function useDelete() 223 | { 224 | $this->method = self::METHOD_DELETE; 225 | 226 | return $this; 227 | } 228 | 229 | /** 230 | * @return $this 231 | */ 232 | public function useGet() 233 | { 234 | $this->method = self::METHOD_GET; 235 | 236 | return $this; 237 | } 238 | 239 | /** 240 | * @return $this 241 | */ 242 | public function usePatch() 243 | { 244 | $this->method = self::METHOD_PATCH; 245 | 246 | return $this; 247 | } 248 | 249 | /** 250 | * @return $this 251 | */ 252 | public function usePost() 253 | { 254 | $this->method = self::METHOD_POST; 255 | 256 | return $this; 257 | } 258 | 259 | /** 260 | * @return $this 261 | */ 262 | public function usePut() 263 | { 264 | $this->method = self::METHOD_PUT; 265 | 266 | return $this; 267 | } 268 | 269 | /** 270 | * @param array|ResponseBehaviourInterface[] $behaviours 271 | * @param Response $response 272 | * @return Response 273 | */ 274 | private function applyBehaviours(array $behaviours, Response $response) 275 | { 276 | foreach ($behaviours as $behaviour) { 277 | $response = $behaviour->behave($response); 278 | } 279 | 280 | return $response; 281 | } 282 | 283 | /** 284 | * @param mixed $data 285 | * @param boolean $convertIt 286 | * @return mixed 287 | */ 288 | private function convertToJsonIfNeeded($data, $convertIt) 289 | { 290 | return ($convertIt) ? json_encode($data) : $data; 291 | } 292 | 293 | /** 294 | * @param string $method 295 | * @param Request $request 296 | * @param string $url 297 | * @param array $parameters 298 | * @param mixed $data 299 | * @return Response 300 | * @throws RuntimeException 301 | */ 302 | private function fetchResponseFromRequestOrThrowRuntimeException($method, Request $request, $url, array $parameters, $data) 303 | { 304 | switch ($method) { 305 | case self::METHOD_DELETE: 306 | $response = $request->delete($url, $parameters, $data); 307 | break; 308 | case self::METHOD_GET: 309 | $response = $request->get($url, $parameters); 310 | break; 311 | case self::METHOD_PATCH: 312 | $response = $request->patch($url, $parameters, $data); 313 | break; 314 | case self::METHOD_POST: 315 | $response = $request->post($url, $parameters, $data); 316 | break; 317 | case self::METHOD_PUT: 318 | $response = $request->put($url, $parameters, $data); 319 | break; 320 | default: 321 | throw new RuntimeException( 322 | 'no http method set' 323 | ); 324 | } 325 | 326 | return $response; 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /source/Builder/BuilderFactory.php: -------------------------------------------------------------------------------- 1 | 4 | * @since 2015-12-09 5 | */ 6 | namespace Net\Bazzline\Component\Curl\Builder; 7 | 8 | use Net\Bazzline\Component\Curl\Dispatcher\DispatcherInterface; 9 | use Net\Bazzline\Component\Curl\FactoryInterface; 10 | use Net\Bazzline\Component\Curl\Request\Request; 11 | use Net\Bazzline\Component\Curl\Request\RequestFactory; 12 | use Net\Bazzline\Component\Toolbox\HashMap\Merge; 13 | 14 | class BuilderFactory implements FactoryInterface 15 | { 16 | /** @var DispatcherInterface */ 17 | private $dispatcher; 18 | 19 | /** @var RequestFactory */ 20 | private $factory; 21 | 22 | /** 23 | * @return Builder|mixed 24 | */ 25 | public function create() 26 | { 27 | $builder = new Builder( 28 | $this->createRequestFromFactory(), 29 | new Merge() 30 | ); 31 | 32 | return $builder; 33 | } 34 | 35 | /** 36 | * @param DispatcherInterface $dispatcher 37 | */ 38 | public function overwriteDispatcher(DispatcherInterface $dispatcher) 39 | { 40 | $this->dispatcher = $dispatcher; 41 | } 42 | 43 | /** 44 | * @param RequestFactory $factory 45 | */ 46 | public function overwriteRequestFactory(RequestFactory $factory) 47 | { 48 | $this->factory = $factory; 49 | } 50 | 51 | /** 52 | * @return Request 53 | */ 54 | protected function createRequestFromFactory() 55 | { 56 | $dispatcher = $this->dispatcher; 57 | $isInvalidFactory = (!($this->factory instanceof RequestFactory)); 58 | $isValidDispatcher = ($dispatcher instanceof DispatcherInterface); 59 | 60 | if ($isInvalidFactory) { 61 | $this->factory = new RequestFactory(); 62 | } 63 | 64 | if ($isValidDispatcher) { 65 | $this->factory->overwriteDispatcher($dispatcher); 66 | } 67 | 68 | $factory = $this->factory; 69 | 70 | return $factory->create(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /source/Dispatcher/Dispatcher.php: -------------------------------------------------------------------------------- 1 | 4 | * @since 2015-12-09 5 | */ 6 | 7 | namespace Net\Bazzline\Component\Curl\Dispatcher; 8 | 9 | use Net\Bazzline\Component\Curl\Response\Response; 10 | 11 | class Dispatcher implements DispatcherInterface 12 | { 13 | /** @var array */ 14 | private $headerLines; 15 | 16 | /** 17 | * @param string $url 18 | * @param array $options 19 | * @return Response 20 | */ 21 | public function dispatch($url, array $options = []) 22 | { 23 | $this->reset(); 24 | $handler = $this->getHandler($url); 25 | $handler = $this->setOptions($handler, $options); 26 | $response = $this->execute($handler); 27 | 28 | return $response; 29 | } 30 | 31 | /** 32 | * @param resource $handler 33 | * @return Response 34 | */ 35 | protected function execute($handler) 36 | { 37 | $content = curl_exec($handler); 38 | $contentType = curl_getinfo($handler, CURLINFO_CONTENT_TYPE); 39 | //@see http://stackoverflow.com/a/10667879 40 | $error = curl_error($handler); 41 | $errorCode = curl_errno($handler); 42 | $statusCode = curl_getinfo($handler, CURLINFO_HTTP_CODE); 43 | //@todo investigate if needed http://www.ivangabriele.com/php-how-to-use-4-methods-delete-get-post-put-in-a-restful-api-client-using-curl/ 44 | //@todo how to handle response code 100 - other header? - http://stackoverflow.com/a/23939785 45 | curl_close($handler); 46 | 47 | return new Response($content, $contentType, $error, $errorCode, $this->headerLines, $statusCode); 48 | } 49 | 50 | /** 51 | * @param string $url 52 | * @return resource 53 | */ 54 | protected function getHandler($url) 55 | { 56 | $handler = curl_init($url); 57 | 58 | return $handler; 59 | } 60 | 61 | /** 62 | * @param resource $handler 63 | * @param array $options 64 | * @return resource 65 | */ 66 | protected function setOptions($handler, array $options) 67 | { 68 | $options[CURLINFO_HEADER_OUT] = 1; 69 | $options[CURLOPT_HEADERFUNCTION] = [ 70 | $this, 71 | 'processHeadLine' 72 | ]; 73 | $options[CURLOPT_RETURNTRANSFER] = true; 74 | 75 | curl_setopt_array($handler, $options); 76 | 77 | return $handler; 78 | } 79 | 80 | 81 | 82 | /** 83 | * @param resource $handler 84 | * @param string $string 85 | * @return int 86 | */ 87 | private function processHeadLine($handler, $string) 88 | { 89 | $delimiter = ':'; 90 | $exploded = explode($delimiter, trim($string)); 91 | $isValid = (count($exploded) === 2); 92 | 93 | if ($isValid) { 94 | $prefix = array_shift($exploded); 95 | $this->headerLines[$prefix] = implode($delimiter, $exploded); //needed because of lines like "Date: Thu, 17 Dec 2015 16:47:42 GMT" 96 | } 97 | 98 | return strlen($string); 99 | } 100 | 101 | private function reset() 102 | { 103 | $this->headerLines = []; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /source/Dispatcher/DispatcherInterface.php: -------------------------------------------------------------------------------- 1 | 4 | * @since: 2015-12-14 5 | */ 6 | 7 | namespace Net\Bazzline\Component\Curl\Dispatcher; 8 | 9 | use Net\Bazzline\Component\Curl\Response\Response; 10 | 11 | interface DispatcherInterface 12 | { 13 | /** 14 | * @param string $url 15 | * @param array $options 16 | * @return Response 17 | */ 18 | public function dispatch($url, array $options = []); 19 | } 20 | -------------------------------------------------------------------------------- /source/Dispatcher/LoggingDispatcher.php: -------------------------------------------------------------------------------- 1 | 4 | * @since 2015-12-21 5 | */ 6 | 7 | namespace Net\Bazzline\Component\Curl\Dispatcher; 8 | 9 | use Net\Bazzline\Component\Curl\Response\Response; 10 | 11 | class LoggingDispatcher extends Dispatcher 12 | { 13 | /** 14 | * @param string $url 15 | * @param array $options 16 | * @return Response 17 | */ 18 | public function dispatch($url, array $options = []) 19 | { 20 | $this->log($url, $options); 21 | 22 | return parent::dispatch($url, $options); 23 | } 24 | 25 | /** 26 | * @param $url 27 | * @param array $options 28 | */ 29 | protected function log($url, array $options) 30 | { 31 | echo 'url: ' . $url . PHP_EOL; 32 | echo 'options: ' . PHP_EOL; 33 | echo var_export($options, true) . PHP_EOL; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/FactoryInterface.php: -------------------------------------------------------------------------------- 1 | prefix() . ': ' . $this->suffix(); 13 | } 14 | 15 | /** 16 | * @return string 17 | */ 18 | abstract protected function prefix(); 19 | 20 | /** 21 | * @return string 22 | */ 23 | abstract protected function suffix(); 24 | } 25 | -------------------------------------------------------------------------------- /source/HeaderLine/AcceptEncoding.php: -------------------------------------------------------------------------------- 1 | 4 | * @since: 2016-09-19 5 | */ 6 | namespace Net\Bazzline\Component\Curl\HeaderLine; 7 | 8 | class AcceptEncoding extends AbstractHeaderLine 9 | { 10 | /** @var string */ 11 | private $prefix; 12 | 13 | /** @var string */ 14 | private $suffix; 15 | 16 | /** 17 | * AcceptEncoding constructor. 18 | * 19 | * @param array|string[] $encodings - e.g. ['gzip', 'deflate'] 20 | */ 21 | public function __construct(array $encodings) 22 | { 23 | $this->prefix = 'Accept-Encoding'; 24 | $this->suffix = implode($encodings); 25 | } 26 | 27 | /** 28 | * @return string 29 | */ 30 | protected function prefix() 31 | { 32 | return $this->prefix; 33 | } 34 | 35 | /** 36 | * @return string 37 | */ 38 | protected function suffix() 39 | { 40 | return $this->suffix; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /source/HeaderLine/AcceptLanguage.php: -------------------------------------------------------------------------------- 1 | 4 | * @since: 2016-09-19 5 | */ 6 | namespace Net\Bazzline\Component\Curl\HeaderLine; 7 | 8 | class AcceptLanguage extends AbstractHeaderLine 9 | { 10 | /** @var string */ 11 | private $prefix; 12 | 13 | /** @var string */ 14 | private $suffix; 15 | 16 | /** 17 | * AcceptLanguage constructor. 18 | * 19 | * @param string $content - compress, gzip | * | compress;q=0.5, gzip;q=1.0 | gzip;q=1.0, identity; q=0.5, *;q=0 20 | */ 21 | public function __construct($content) 22 | { 23 | $this->prefix = 'Accept-Encoding'; 24 | $this->suffix = $content; 25 | } 26 | 27 | /** 28 | * @return string 29 | */ 30 | protected function prefix() 31 | { 32 | return $this->prefix; 33 | } 34 | 35 | /** 36 | * @return string 37 | */ 38 | protected function suffix() 39 | { 40 | return $this->suffix; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /source/HeaderLine/ContentTypeIsUtf8Form.php: -------------------------------------------------------------------------------- 1 | 4 | * @since: 2016-09-19 5 | */ 6 | namespace Net\Bazzline\Component\Curl\HeaderLine; 7 | 8 | class Custom extends AbstractHeaderLine 9 | { 10 | /** @var string */ 11 | private $prefix; 12 | 13 | /** @var string */ 14 | private $suffix; 15 | 16 | /** 17 | * Custom constructor. 18 | * 19 | * @param string $identifier - e.g. My-Header-Line - "X-" will be prefixed automatically 20 | * @param string $value - e.g. bazzline 21 | */ 22 | public function __construct($identifier, $value) 23 | { 24 | $this->prefix = 'X-' . $identifier; 25 | $this->suffix = $value; 26 | } 27 | 28 | /** 29 | * @return string 30 | */ 31 | protected function prefix() 32 | { 33 | return $this->prefix; 34 | } 35 | 36 | /** 37 | * @return string 38 | */ 39 | protected function suffix() 40 | { 41 | return $this->suffix; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /source/HeaderLine/HeaderLineInterface.php: -------------------------------------------------------------------------------- 1 | 4 | * @since 2015-12-08 5 | */ 6 | namespace Net\Bazzline\Component\Curl\HeaderLine; 7 | 8 | interface HeaderLineInterface 9 | { 10 | /** 11 | * @return string 12 | */ 13 | public function line(); 14 | } 15 | -------------------------------------------------------------------------------- /source/Option/AbstractAuthentication.php: -------------------------------------------------------------------------------- 1 | valueForThisOption = $valueForThisOption; 16 | } 17 | 18 | /** 19 | * @return array 20 | */ 21 | public function value() 22 | { 23 | return $this->valueForThisOption; 24 | } 25 | } -------------------------------------------------------------------------------- /source/Option/AbstractSetOptionClosureValue.php: -------------------------------------------------------------------------------- 1 | valueForThisOption = $valueForThisOption; 18 | } 19 | 20 | /** 21 | * @return Closure 22 | */ 23 | public function value() 24 | { 25 | return $this->valueForThisOption; 26 | } 27 | } -------------------------------------------------------------------------------- /source/Option/AbstractSetOptionIntValue.php: -------------------------------------------------------------------------------- 1 | valueForThisOption = $valueForThisOption; 16 | } 17 | 18 | /** 19 | * @return int 20 | */ 21 | public function value() 22 | { 23 | return $this->valueForThisOption; 24 | } 25 | } -------------------------------------------------------------------------------- /source/Option/AbstractSetOptionMixedValue.php: -------------------------------------------------------------------------------- 1 | valueForThisOption = $valueForThisOption; 16 | } 17 | 18 | /** 19 | * @return mixed 20 | */ 21 | public function value() 22 | { 23 | return $this->valueForThisOption; 24 | } 25 | } -------------------------------------------------------------------------------- /source/Option/AbstractSetOptionStreamValue.php: -------------------------------------------------------------------------------- 1 | valueForThisOption = $valueForThisOption; 16 | } 17 | 18 | /** 19 | * @return resource 20 | */ 21 | public function value() 22 | { 23 | return $this->valueForThisOption; 24 | } 25 | } -------------------------------------------------------------------------------- /source/Option/AbstractSetOptionStringValue.php: -------------------------------------------------------------------------------- 1 | valueForThisOption = $valueForThisOption; 16 | } 17 | 18 | /** 19 | * @return string 20 | */ 21 | public function value() 22 | { 23 | return $this->valueForThisOption; 24 | } 25 | } -------------------------------------------------------------------------------- /source/Option/AbstractSetOptionToFalse.php: -------------------------------------------------------------------------------- 1 | password = $password; 22 | $this->username = $username; 23 | } 24 | 25 | /** 26 | * @return int 27 | */ 28 | public function identifier() 29 | { 30 | return CURLOPT_USERPWD; 31 | } 32 | 33 | /** 34 | * @return mixed 35 | */ 36 | public function value() 37 | { 38 | return $this->username . ':' . $this->password; 39 | } 40 | } -------------------------------------------------------------------------------- /source/Option/Authorization/EnableNetrc.php: -------------------------------------------------------------------------------- 1 | 4 | * @since 2015-12-08 5 | */ 6 | namespace Net\Bazzline\Component\Curl\Option; 7 | 8 | interface OptionInterface 9 | { 10 | /** 11 | * @return int 12 | */ 13 | public function identifier(); 14 | 15 | /** 16 | * @return mixed 17 | */ 18 | public function value(); 19 | } -------------------------------------------------------------------------------- /source/Option/Security/DisableSslVerifyHost.php: -------------------------------------------------------------------------------- 1 | key = $key; 20 | $this->value = $value; 21 | } 22 | 23 | /** 24 | * @return int 25 | */ 26 | public function identifier() 27 | { 28 | return $this->key; 29 | } 30 | 31 | /** 32 | * @return mixed 33 | */ 34 | public function value() 35 | { 36 | return $this->value; 37 | } 38 | } -------------------------------------------------------------------------------- /source/Option/Transfer/DisableDnsUseGlobalCache.php: -------------------------------------------------------------------------------- 1 | 4 | * @since: 2015-12-09 5 | */ 6 | 7 | namespace Net\Bazzline\Component\Curl\Request; 8 | 9 | use Net\Bazzline\Component\Curl\Dispatcher\DispatcherInterface; 10 | use Net\Bazzline\Component\Curl\HeaderLine\HeaderLineInterface; 11 | use Net\Bazzline\Component\Curl\Option\OptionInterface; 12 | use Net\Bazzline\Component\Curl\Response\Response; 13 | use Net\Bazzline\Component\Toolbox\HashMap\Merge; 14 | 15 | class Request 16 | { 17 | //@see: http://developer.sugarcrm.com/2013/08/30/doing-put-and-delete-with-curl-in-php/ 18 | const HTTP_METHOD_DELETE = 'DELETE'; 19 | const HTTP_METHOD_GET = 'GET'; 20 | const HTTP_METHOD_PATCH = 'PATCH'; 21 | const HTTP_METHOD_POST = 'POST'; 22 | const HTTP_METHOD_PUT = 'PUT'; 23 | 24 | /** @var array */ 25 | private $defaultHeaderLines = []; 26 | 27 | /** @var array */ 28 | private $defaultOptions = []; 29 | 30 | /** @var DispatcherInterface */ 31 | private $dispatcher; 32 | 33 | /** @var array */ 34 | private $headerLines = []; 35 | 36 | /** @var Merge */ 37 | private $merge; 38 | 39 | /** @var array */ 40 | private $options = []; 41 | 42 | /** 43 | * @param DispatcherInterface $dispatcher 44 | * @param Merge $merge 45 | * @param array $defaultHeaderLines 46 | * => 47 | * @param array $defaultOptions 48 | * either: 49 | * => 50 | * or: 51 | * => 52 | */ 53 | public function __construct(DispatcherInterface $dispatcher, Merge $merge, array $defaultHeaderLines = [], array $defaultOptions = []) 54 | { 55 | $this->defaultHeaderLines = $defaultHeaderLines; 56 | $this->defaultOptions = $defaultOptions; 57 | $this->dispatcher = $dispatcher; 58 | $this->merge = $merge; 59 | } 60 | 61 | /** 62 | * @return Request 63 | */ 64 | public function __clone() 65 | { 66 | return new self( 67 | $this->dispatcher, 68 | $this->merge, 69 | $this->defaultHeaderLines, 70 | $this->defaultOptions 71 | ); 72 | } 73 | 74 | /** 75 | * @param HeaderLineInterface $line 76 | */ 77 | public function addHeaderLine(HeaderLineInterface $line) 78 | { 79 | $this->headerLines[] = $line->line(); 80 | } 81 | 82 | /** 83 | * @param OptionInterface $option 84 | */ 85 | public function addOption(OptionInterface $option) 86 | { 87 | $this->options[$option->identifier()] = $option->value(); 88 | } 89 | 90 | /** 91 | * @param string $line - CURLOPT_* - see: http://php.net/manual/en/function.curl-setopt.php 92 | */ 93 | public function addRawHeaderLine($line) 94 | { 95 | $this->headerLines[] = $line; 96 | } 97 | 98 | /** 99 | * @param string $key - CURLOPT_* - see: http://php.net/manual/en/function.curl-setopt.php 100 | * @param mixed $value 101 | */ 102 | public function addRawOption($key, $value) 103 | { 104 | $this->options[$key] = $value; 105 | } 106 | 107 | /** 108 | * @param string $url 109 | * @param array $parameters 110 | * @return Response 111 | */ 112 | public function get($url, array $parameters = []) 113 | { 114 | return $this->execute( 115 | $url, 116 | self::HTTP_METHOD_GET, 117 | $parameters, 118 | [] 119 | ); 120 | } 121 | 122 | /** 123 | * @param string $url 124 | * @param array $parameters 125 | * @param null|string|array $data 126 | * @return Response 127 | */ 128 | public function post($url, array $parameters = [], $data = null) 129 | { 130 | return $this->execute( 131 | $url, 132 | self::HTTP_METHOD_POST, 133 | $parameters, 134 | $data 135 | ); 136 | } 137 | 138 | /** 139 | * @param string $url 140 | * @param array $parameters 141 | * @param null|string|array $data 142 | * @return Response 143 | */ 144 | public function put($url, array $parameters = [], $data = null) 145 | { 146 | return $this->execute( 147 | $url, 148 | self::HTTP_METHOD_PUT, 149 | $parameters, 150 | $data 151 | ); 152 | } 153 | 154 | /** 155 | * @param string $url 156 | * @param array $parameters 157 | * @param null|string|array $data 158 | * @return Response 159 | */ 160 | public function patch($url, array $parameters = [], $data = null) 161 | { 162 | return $this->execute( 163 | $url, 164 | self::HTTP_METHOD_PATCH, 165 | $parameters, 166 | $data 167 | ); 168 | } 169 | 170 | /** 171 | * @param string $url 172 | * @param array $parameters 173 | * @param null|string|array $data 174 | * @return Response 175 | */ 176 | public function delete($url, array $parameters = [], $data = null) 177 | { 178 | return $this->execute( 179 | $url, 180 | self::HTTP_METHOD_DELETE, 181 | $parameters, 182 | $data 183 | ); 184 | } 185 | 186 | /** 187 | * @see: https://de.wikipedia.org/wiki/Representational_State_Transfer 188 | public function head($url) 189 | { 190 | } 191 | public function options($url) 192 | { 193 | } 194 | public function trace($url) 195 | { 196 | } 197 | */ 198 | 199 | /** 200 | * @param bool|false $alsoTheDefaults 201 | */ 202 | public function reset($alsoTheDefaults = false) 203 | { 204 | $this->headerLines = []; 205 | $this->options = []; 206 | 207 | if ($alsoTheDefaults) { 208 | $this->defaultHeaderLines = []; 209 | $this->defaultOptions = []; 210 | } 211 | } 212 | 213 | /** 214 | * @param array $options 215 | * @param mixed $data 216 | * @return array 217 | */ 218 | private function addDataToTheOptionsIfDataIsValid(array $options, $data) 219 | { 220 | $isDataProvided = (!is_null($data)); 221 | 222 | if ($isDataProvided) { 223 | $dataIsNotFromTypeScalar = (!is_scalar($data)); 224 | 225 | if ($dataIsNotFromTypeScalar) { 226 | $data = http_build_query($data); 227 | } 228 | 229 | $dataStringContainsContent = (strlen($data) > 0); 230 | 231 | if ($dataStringContainsContent) { 232 | $options[CURLOPT_POSTFIELDS] = $data; //@see: http://www.lornajane.net/posts/2009/putting-data-fields-with-php-curl 233 | } 234 | } 235 | 236 | return $options; 237 | } 238 | 239 | /** 240 | * @param array $parameters 241 | * @param string $url 242 | * @return string 243 | */ 244 | private function addParametersToTheUrlIfParametersAreProvided(array $parameters, $url) 245 | { 246 | $areParametersProvided = (!empty($parameters)); 247 | 248 | if ($areParametersProvided) { 249 | $parametersAsString = http_build_query($parameters); 250 | $isParameterStringValid = (strlen($parametersAsString) > 0); 251 | 252 | if ($isParameterStringValid) { 253 | $urlWithParameters = $url . '?' . http_build_query($parameters); 254 | } else { 255 | $urlWithParameters = $url; 256 | } 257 | } else { 258 | $urlWithParameters = $url; 259 | } 260 | 261 | return $urlWithParameters; 262 | } 263 | 264 | /** 265 | * @param string $url 266 | * @param string $method 267 | * @param null|array $parameters 268 | * @param null|string|array $data 269 | * @return Response 270 | */ 271 | private function execute($url, $method, array $parameters = [], $data = null) 272 | { 273 | //begin of dependencies 274 | $dispatcher = $this->dispatcher; 275 | $merge = $this->merge; 276 | $headerLines = $merge($this->headerLines, $this->defaultHeaderLines); 277 | $options = $merge($this->options, $this->defaultOptions); 278 | $headerLines[] = 'X-HTTP-Method-Override: ' . $method; //@see: http://tr.php.net/curl_setopt#109634 279 | //end of dependencies 280 | 281 | //begin of business logic 282 | $options[CURLOPT_CUSTOMREQUEST] = $method; //@see: http://tr.php.net/curl_setopt#109634 283 | $options[CURLOPT_HTTPHEADER] = $headerLines; 284 | //@todo what about binary transfer? 285 | 286 | $options = $this->addDataToTheOptionsIfDataIsValid($options, $data); 287 | $urlWithParameters = $this->addParametersToTheUrlIfParametersAreProvided($parameters, $url); 288 | $response = $dispatcher->dispatch($urlWithParameters, $options); 289 | //end of business logic 290 | 291 | return $response; 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /source/Request/RequestFactory.php: -------------------------------------------------------------------------------- 1 | createDefaultHeaderLines( 25 | $this->getDefaultRawHeaderLine(), 26 | $this->getDefaultHeaderLines() 27 | ); 28 | 29 | $defaultOptions = $this->createDefaultOptions( 30 | $this->getDefaultRawOptions(), 31 | $this->getDefaultOptions() 32 | ); 33 | 34 | $request = new Request( 35 | $this->getDispatcher(), 36 | new Merge(), 37 | $defaultHeaderLines, 38 | $defaultOptions 39 | ); 40 | 41 | return $request; 42 | } 43 | 44 | /** 45 | * @param DispatcherInterface $dispatcher 46 | */ 47 | public function overwriteDispatcher(DispatcherInterface $dispatcher) 48 | { 49 | $this->dispatcher = $dispatcher; 50 | } 51 | 52 | /** 53 | * @return DispatcherInterface 54 | */ 55 | protected function getDispatcher() 56 | { 57 | $createDispatcher = !($this->dispatcher instanceof DispatcherInterface); 58 | 59 | if ($createDispatcher) { 60 | $this->dispatcher = $this->getNewDispatcher(); 61 | } 62 | 63 | return $this->dispatcher; 64 | } 65 | 66 | /** 67 | * @return DispatcherInterface 68 | */ 69 | protected function getNewDispatcher() 70 | { 71 | return new Dispatcher(); 72 | } 73 | 74 | /** 75 | * @return array|HeaderLineInterface[] 76 | */ 77 | protected function getDefaultHeaderLines() 78 | { 79 | return []; 80 | } 81 | 82 | /** 83 | * @return array 84 | */ 85 | protected function getDefaultRawHeaderLine() 86 | { 87 | return []; 88 | } 89 | 90 | /** 91 | * @return array|OptionInterface[] 92 | */ 93 | protected function getDefaultOptions() 94 | { 95 | return [ 96 | new SetTimeOutInSeconds(10) 97 | ]; 98 | } 99 | 100 | /** 101 | * @return array 102 | */ 103 | protected function getDefaultRawOptions() 104 | { 105 | return [ 106 | CURLOPT_AUTOREFERER => true, 107 | CURLOPT_CONNECTTIMEOUT => 5, 108 | CURLOPT_FOLLOWLOCATION => true, 109 | CURLOPT_MAXREDIRS => 10, 110 | CURLOPT_USERAGENT => 'net/bazzline curl component for php' 111 | ]; 112 | } 113 | 114 | /** 115 | * @param array $rawHeaderLines 116 | * @param array|HeaderLineInterface[] $headerLines 117 | * @return array 118 | */ 119 | private function createDefaultHeaderLines(array $rawHeaderLines, array $headerLines) 120 | { 121 | $defaultHeaderLines = $rawHeaderLines; 122 | 123 | foreach ($headerLines as $headerLine) { 124 | $defaultHeaderLines[] = $headerLine->line(); 125 | } 126 | 127 | return $defaultHeaderLines; 128 | } 129 | 130 | /** 131 | * @param array $rawOptions 132 | * @param array|OptionInterface[] $options 133 | * @return array 134 | */ 135 | private function createDefaultOptions(array $rawOptions, array $options) 136 | { 137 | $defaultOptions = $rawOptions; 138 | 139 | foreach ($options as $option) { 140 | $defaultOptions[$option->identifier()] = $option->value(); 141 | } 142 | 143 | return $defaultOptions; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /source/Response/Response.php: -------------------------------------------------------------------------------- 1 | 4 | * @since: 2015-12-09 5 | */ 6 | 7 | namespace Net\Bazzline\Component\Curl\Response; 8 | 9 | use InvalidArgumentException; 10 | 11 | class Response 12 | { 13 | const ARRAY_KEY_CONTENT = 'content'; 14 | const ARRAY_KEY_CONTENT_TYPE = 'content_type'; 15 | const ARRAY_KEY_ERROR = 'error'; 16 | const ARRAY_KEY_ERROR_CODE = 'error_code'; 17 | const ARRAY_KEY_STATUS_CODE = 'status_code'; 18 | 19 | /** @var null|mixed */ 20 | private $content; 21 | 22 | /** @var null|string */ 23 | private $contentType; 24 | 25 | /** @var null|string */ 26 | private $error; 27 | 28 | /** @var null|int */ 29 | private $errorCode; 30 | 31 | /** @var array */ 32 | private $headerLines; 33 | 34 | /** @var null|int */ 35 | private $statusCode; 36 | 37 | /** 38 | * @param mixed $content 39 | * @param string $contentType 40 | * @param string $error 41 | * @param int $errorCode 42 | * @param array $headerLines 43 | * @param int $statusCode 44 | */ 45 | public function __construct($content, $contentType, $error, $errorCode, array $headerLines, $statusCode) 46 | { 47 | $this->content = $content; 48 | $this->contentType = $contentType; 49 | $this->error = $error; 50 | $this->errorCode = $errorCode; 51 | $this->headerLines = $headerLines; 52 | $this->statusCode = $statusCode; 53 | } 54 | 55 | /** 56 | * @return null|mixed 57 | */ 58 | public function content() 59 | { 60 | return $this->content; 61 | } 62 | 63 | /** 64 | * @return null|string 65 | */ 66 | public function contentType() 67 | { 68 | return $this->contentType; 69 | } 70 | 71 | /** 72 | * @param string $prefix 73 | * @return null|string 74 | * @throws InvalidArgumentException 75 | */ 76 | public function headerLine($prefix) 77 | { 78 | $valueIsNotAvailable = (!isset($this->headerLines[$prefix])); 79 | 80 | if ($valueIsNotAvailable) { 81 | throw new InvalidArgumentException( 82 | 'no headline available for prefix: "' . $prefix . '"' 83 | ); 84 | } 85 | 86 | return $this->headerLines[$prefix]; 87 | } 88 | 89 | /** 90 | * @return array 91 | */ 92 | public function headerLines() 93 | { 94 | return $this->headerLines; 95 | } 96 | 97 | /** 98 | * @return null|string 99 | */ 100 | public function error() 101 | { 102 | return $this->error; 103 | } 104 | 105 | /** 106 | * @return null|int 107 | * @see: http://curl.haxx.se/libcurl/c/libcurl-errors.html 108 | */ 109 | public function errorCode() 110 | { 111 | return $this->errorCode; 112 | } 113 | 114 | /** 115 | * @return null|int 116 | */ 117 | public function statusCode() 118 | { 119 | return $this->statusCode; 120 | } 121 | 122 | /** 123 | * @return array 124 | */ 125 | public function convertIntoAnArray() 126 | { 127 | return [ 128 | self::ARRAY_KEY_CONTENT => $this->content, 129 | self::ARRAY_KEY_CONTENT_TYPE => $this->contentType, 130 | self::ARRAY_KEY_ERROR => $this->error, 131 | self::ARRAY_KEY_ERROR_CODE => $this->errorCode, 132 | self::ARRAY_KEY_STATUS_CODE => $this->statusCode 133 | ]; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /source/ResponseBehaviour/ConvertJsonToArrayBehaviour.php: -------------------------------------------------------------------------------- 1 | 4 | * @since 2015-12-09 5 | */ 6 | namespace Net\Bazzline\Component\Curl\ResponseBehaviour; 7 | 8 | use Exception; 9 | use Net\Bazzline\Component\Curl\Response\Response; 10 | 11 | class ConvertJsonToArrayBehaviour implements ResponseBehaviourInterface 12 | { 13 | /** 14 | * Since the Response is immutable, each behaviour has to return a new 15 | * Response instance 16 | * 17 | * @param Response $response 18 | * @return Response 19 | * @throws Exception 20 | */ 21 | public function behave(Response $response) 22 | { 23 | return new Response( 24 | json_decode($response->content(), true), 25 | $response->contentType(), 26 | $response->error(), 27 | $response->errorCode(), 28 | $response->headerLines(), 29 | $response->statusCode() 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /source/ResponseBehaviour/ResponseBehaviourInterface.php: -------------------------------------------------------------------------------- 1 | 4 | * @since 2015-12-08 5 | */ 6 | namespace Net\Bazzline\Component\Curl\ResponseBehaviour; 7 | 8 | use Exception; 9 | use Net\Bazzline\Component\Curl\Response\Response; 10 | 11 | interface ResponseBehaviourInterface 12 | { 13 | /** 14 | * Since the Response is immutable, each behaviour has to return a new 15 | * Response instance 16 | * 17 | * @param Response $response 18 | * @return Response 19 | * @throws Exception 20 | */ 21 | public function behave(Response $response); 22 | } 23 | -------------------------------------------------------------------------------- /source/ResponseBehaviour/ThrowRuntimeExceptionIfStatusCodeIsAboveTheLimitBehaviour.php: -------------------------------------------------------------------------------- 1 | 4 | * @since 2015-12-09 5 | */ 6 | namespace Net\Bazzline\Component\Curl\ResponseBehaviour; 7 | 8 | use Exception; 9 | use Net\Bazzline\Component\Curl\Response\Response; 10 | use RuntimeException; 11 | 12 | class ThrowRuntimeExceptionIfStatusCodeIsAboveTheLimitBehaviour implements ResponseBehaviourInterface 13 | { 14 | /** @var int */ 15 | private $statusCode; 16 | 17 | /** 18 | * @param int $firstStatusCodeThatIsOverTheLimit 19 | */ 20 | public function __construct($firstStatusCodeThatIsOverTheLimit = 400) 21 | { 22 | $this->statusCode = (int) $firstStatusCodeThatIsOverTheLimit; 23 | } 24 | 25 | /** 26 | * Since the Response is immutable, each behaviour has to return a new 27 | * Response instance 28 | * 29 | * @param Response $response 30 | * @return Response 31 | * @throws Exception|RuntimeException 32 | */ 33 | public function behave(Response $response) 34 | { 35 | $statusCodeIsToHigh = ($response->statusCode() > $this->statusCode); 36 | 37 | if ($statusCodeIsToHigh) { 38 | throw new RuntimeException( 39 | 'limit of status code exceeded. dumping response: ' . 40 | implode(', ', $response->convertIntoAnArray()) 41 | ); 42 | } 43 | 44 | return $response; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/AbstractTestCase.php: -------------------------------------------------------------------------------- 1 | 4 | * @since: 2015-12-14 5 | */ 6 | 7 | namespace Test\Net\Bazzline\Component\Curl; 8 | 9 | use Mockery; 10 | use Net\Bazzline\Component\Curl\Builder\Builder; 11 | use Net\Bazzline\Component\Curl\Dispatcher\DispatcherInterface; 12 | use Net\Bazzline\Component\Curl\Request\Request; 13 | use Net\Bazzline\Component\Curl\Response\Response; 14 | use Net\Bazzline\Component\Toolbox\HashMap\Merge; 15 | use PHPUnit_Framework_TestCase; 16 | 17 | abstract class AbstractTestCase extends PHPUnit_Framework_TestCase 18 | { 19 | 20 | /** 21 | * @param string $method 22 | * @param array $headerLines 23 | * @param array $options 24 | * @param null|mixed $data 25 | * @return array 26 | */ 27 | protected function buildDispatcherOptions($method, array $headerLines = [], array $options = [], $data = null) 28 | { 29 | $isDataProvided = (!is_null($data)); 30 | $headerLines[] = 'X-HTTP-Method-Override: ' . $method; //@see: http://tr.php.net/curl_setopt#109634 31 | $dispatcherOptions = $options; 32 | 33 | $dispatcherOptions[CURLOPT_CUSTOMREQUEST] = $method; 34 | $dispatcherOptions[CURLOPT_HTTPHEADER] = $headerLines; 35 | 36 | if ($isDataProvided) { 37 | $dispatcherOptions[CURLOPT_POSTFIELDS] = $data; 38 | } 39 | 40 | return $dispatcherOptions; 41 | } 42 | 43 | /** 44 | * @return Mockery\MockInterface|\Net\Bazzline\Component\Curl\ResponseBehaviour\ResponseBehaviourInterface' 45 | */ 46 | protected function getMockOfTheBehaviour() 47 | { 48 | return Mockery::mock('Net\Bazzline\Component\Curl\ResponseBehaviour\ResponseBehaviourInterface'); 49 | } 50 | 51 | /** 52 | * @return Mockery\MockInterface|DispatcherInterface 53 | */ 54 | protected function getMockOfTheDispatcher() 55 | { 56 | return Mockery::mock('Net\Bazzline\Component\Curl\Dispatcher\DispatcherInterface'); 57 | } 58 | 59 | /** 60 | * @param Request $request 61 | * @param array $defaultResponseBehaviours 62 | * @return Builder 63 | */ 64 | protected function getNewBuilder(Request $request, array $defaultResponseBehaviours = []) 65 | { 66 | return new Builder($request, new Merge(), $defaultResponseBehaviours); 67 | } 68 | 69 | /** 70 | * @param DispatcherInterface $dispatcher 71 | * @param array $defaultHeaderLines 72 | * @param array $defaultOptions 73 | * @return Request 74 | */ 75 | protected function getNewRequest(DispatcherInterface $dispatcher, array $defaultHeaderLines = [], array $defaultOptions = []) 76 | { 77 | return new Request($dispatcher, new Merge(), $defaultHeaderLines, $defaultOptions); 78 | } 79 | 80 | /** 81 | * @param mixed $content 82 | * @param string $contentType 83 | * @param string $error 84 | * @param int $errorCode 85 | * @param array $headLines 86 | * @param int $statusCode 87 | * @return Response 88 | */ 89 | protected function getNewResponse($content = 'example content', $contentType = 'example content type', $error = '', $errorCode = 0, array $headLines = [], $statusCode = 200) 90 | { 91 | return new Response($content, $contentType, $error, $errorCode, $headLines, $statusCode); 92 | } 93 | 94 | /** 95 | * @return string 96 | */ 97 | protected function getUrl() 98 | { 99 | return 'http://www.foo.bar'; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /test/Builder/BuilderTest.php: -------------------------------------------------------------------------------- 1 | 4 | * @since 2015-12-16 5 | */ 6 | namespace Test\Net\Bazzline\Component\Curl\Builder; 7 | 8 | use Test\Net\Bazzline\Component\Curl\AbstractTestCase; 9 | 10 | class BuilderTest extends AbstractTestCase 11 | { 12 | public function testFetchTheResponse() 13 | { 14 | $dispatcher = $this->getMockOfTheDispatcher(); 15 | $request = $this->getNewRequest($dispatcher); 16 | $builder = $this->getNewBuilder($request); 17 | $response = $this->getNewResponse(); 18 | 19 | $dispatcher->shouldReceive('dispatch') 20 | ->with( 21 | $this->getUrl(), 22 | $this->buildDispatcherOptions('PUT') 23 | ) 24 | ->andReturn($response) 25 | ->once(); 26 | 27 | $builder->onTheUrl($this->getUrl()); 28 | $builder->usePut(); 29 | 30 | $builder->andFetchTheResponse(); 31 | } 32 | 33 | public function testFetchTheResponseAsJson() 34 | { 35 | $dispatcher = $this->getMockOfTheDispatcher(); 36 | $request = $this->getNewRequest($dispatcher); 37 | $builder = $this->getNewBuilder($request); 38 | $response = $this->getNewResponse(); 39 | 40 | $dispatcher->shouldReceive('dispatch') 41 | ->with( 42 | $this->getUrl(), 43 | $this->buildDispatcherOptions( 44 | 'PUT', 45 | [ 46 | 'Content-Type: application/json; charset=UTF-8' 47 | ], 48 | [], 49 | json_encode(null) 50 | ) 51 | ) 52 | ->andReturn($response) 53 | ->once(); 54 | 55 | $builder->asJson(); 56 | $builder->onTheUrl($this->getUrl()); 57 | $builder->usePut(); 58 | 59 | $builder->andFetchTheResponse(); 60 | } 61 | 62 | public function testFetchTheResponseWithBehaviours() 63 | { 64 | $behaviour = $this->getMockOfTheBehaviour(); 65 | $dispatcher = $this->getMockOfTheDispatcher(); 66 | $request = $this->getNewRequest($dispatcher); 67 | $builder = $this->getNewBuilder($request); 68 | $response = $this->getNewResponse(); 69 | 70 | $behaviour->shouldReceive('behave') 71 | ->with($response) 72 | ->andReturn($response) 73 | ->once(); 74 | 75 | $dispatcher->shouldReceive('dispatch') 76 | ->with( 77 | $this->getUrl(), 78 | $this->buildDispatcherOptions('PUT') 79 | ) 80 | ->andReturn($response) 81 | ->once(); 82 | 83 | $builder->onTheUrl($this->getUrl()); 84 | $builder->usePut(); 85 | $builder->withTheResponseBehaviour($behaviour); 86 | 87 | $builder->andFetchTheResponse(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/Request/RequestTest.php: -------------------------------------------------------------------------------- 1 | 4 | * @since: 2015-12-14 5 | */ 6 | 7 | namespace Test\Net\Bazzline\Component\Curl\Request; 8 | 9 | use Net\Bazzline\Component\Curl\HeaderLine\ContentTypeIsUtf8Json; 10 | use Net\Bazzline\Component\Curl\Option\Behaviour\SetTimeOutInSeconds; 11 | use stdClass; 12 | use Test\Net\Bazzline\Component\Curl\AbstractTestCase; 13 | 14 | class RequestTest extends AbstractTestCase 15 | { 16 | /** 17 | * @return array 18 | */ 19 | public function testCaseWithUrlParameters() 20 | { 21 | $url = $this->getUrl(); 22 | 23 | return [ 24 | 'with parameters' => [ 25 | 'parameters' => [], 26 | 'url' => $url, 27 | 'expected_url' => $url 28 | ], 29 | 'with parameter parameter only' => [ 30 | 'parameters' => [ 31 | 'key' 32 | ], 33 | 'url' => $url, 34 | 'expected_url' => $url . '?0=key' 35 | ], 36 | 'with parameter key only' => [ 37 | 'parameters' => [ 38 | 'key' => null 39 | ], 40 | 'url' => $url, 41 | 'expected_url' => $url 42 | ], 43 | 'with one parameter' => [ 44 | 'parameters' => [ 45 | 'key' => 'value' 46 | ], 47 | 'url' => $url, 48 | 'expected_url' => $url . '?key=value' 49 | ], 50 | 'with two parameter' => [ 51 | 'parameters' => [ 52 | 'one' => 'value', 53 | 'two' => 'value' 54 | ], 55 | 'url' => $url, 56 | 'expected_url' => $url . '?one=value&two=value' 57 | ], 58 | 'with multiple parameter' => [ 59 | 'parameters' => [ 60 | 'one' => 'value', 61 | 'two' => 'value', 62 | 'three' => 'value' 63 | ], 64 | 'url' => $url, 65 | 'expected_url' => $url . '?one=value&two=value&three=value' 66 | ], 67 | 'with nested parameter' => [ 68 | 'parameters' => [ 69 | 'key' => [ 70 | 'one', 71 | 'two', 72 | 'three' 73 | ] 74 | ], 75 | 'url' => $url, 76 | 'expected_url' => $url . '?key%5B0%5D=one&key%5B1%5D=two&key%5B2%5D=three' 77 | ] 78 | ]; 79 | } 80 | 81 | /** 82 | * @return array 83 | */ 84 | public function testCaseWithUrlParametersAndData() 85 | { 86 | $testCaseTemplates = $this->testCaseWithUrlParameters(); 87 | $testCases = []; 88 | $object = new stdClass(); 89 | 90 | $object->bar = 'foo'; 91 | $object->foo = 'bar'; 92 | 93 | foreach ($testCaseTemplates as $name => $template) { 94 | $testCases[$name . ' without data'] = [ 95 | 'parameters' => $template['parameters'], 96 | 'data' => null, 97 | 'url' => $template['url'], 98 | 'expected_url' => $template['expected_url'], 99 | 'expected_data' => null 100 | ]; 101 | $testCases[$name . ' with int as data'] = [ 102 | 'parameters' => $template['parameters'], 103 | 'data' => 42, 104 | 'url' => $template['url'], 105 | 'expected_url' => $template['expected_url'], 106 | 'expected_data' => 42 107 | ]; 108 | $testCases[$name . ' with string as data'] = [ 109 | 'parameters' => $template['parameters'], 110 | 'data' => 'there is no foo without a bar', 111 | 'url' => $template['url'], 112 | 'expected_url' => $template['expected_url'], 113 | 'expected_data' => 'there is no foo without a bar' 114 | ]; 115 | $testCases[$name . ' with array as data'] = [ 116 | 'parameters' => $template['parameters'], 117 | 'data' => [ 118 | 'there' => 'is', 119 | 'no' => 'foo', 120 | 'without' => 'a', 121 | 'bar' 122 | ], 123 | 'url' => $template['url'], 124 | 'expected_url' => $template['expected_url'], 125 | 'expected_data' => 'there=is&no=foo&without=a&0=bar' 126 | ]; 127 | $testCases[$name . ' with object as data'] = [ 128 | 'parameters' => $template['parameters'], 129 | 'data' => $object, 130 | 'url' => $template['url'], 131 | 'expected_url' => $template['expected_url'], 132 | 'expected_data' => 'bar=foo&foo=bar' 133 | ]; 134 | } 135 | 136 | return $testCases; 137 | } 138 | 139 | public function testClone() 140 | { 141 | $dispatcher = $this->getMockOfTheDispatcher(); 142 | $headerLines = [ 143 | 'foo: bar' 144 | ]; 145 | $options = [ 146 | CURLOPT_AUTOREFERER => true 147 | ]; 148 | $request = $this->getNewRequest($dispatcher, $headerLines, $options); 149 | $response = $this->getNewResponse(); 150 | 151 | $dispatcher->shouldReceive('dispatch') 152 | ->with( 153 | $this->getUrl(), 154 | $this->buildDispatcherOptions('PUT', $headerLines, $options) 155 | ) 156 | ->andReturn($response) 157 | ->once(); 158 | 159 | $clonedRequest = clone $request; 160 | 161 | $clonedRequest->put($this->getUrl()); 162 | } 163 | 164 | public function testAddHeaderLine() 165 | { 166 | $dispatcher = $this->getMockOfTheDispatcher(); 167 | $headerLine = new ContentTypeIsUtf8Json(); 168 | $request = $this->getNewRequest($dispatcher, [], []); 169 | $response = $this->getNewResponse(); 170 | 171 | $request->addHeaderLine($headerLine); 172 | 173 | $dispatcher->shouldReceive('dispatch') 174 | ->with( 175 | $this->getUrl(), 176 | $this->buildDispatcherOptions( 177 | 'PUT', 178 | [ 179 | $headerLine->line() 180 | ], 181 | [] 182 | ) 183 | ) 184 | ->andReturn($response) 185 | ->once(); 186 | 187 | $request->put($this->getUrl()); 188 | } 189 | 190 | public function testAddOption() 191 | { 192 | $dispatcher = $this->getMockOfTheDispatcher(); 193 | $option = new SetTimeOutInSeconds(__LINE__); 194 | $request = $this->getNewRequest($dispatcher, [], []); 195 | $response = $this->getNewResponse(); 196 | 197 | $request->addOption($option); 198 | 199 | $dispatcher->shouldReceive('dispatch') 200 | ->with( 201 | $this->getUrl(), 202 | $this->buildDispatcherOptions( 203 | 'PUT', 204 | [], 205 | [ 206 | $option->identifier() => $option->value() 207 | ] 208 | ) 209 | ) 210 | ->andReturn($response) 211 | ->once(); 212 | 213 | $request->put($this->getUrl()); 214 | } 215 | 216 | public function testAddRawHeaderLine() 217 | { 218 | $dispatcher = $this->getMockOfTheDispatcher(); 219 | $headerLine = 'foo: bar'; 220 | $request = $this->getNewRequest($dispatcher, [], []); 221 | $response = $this->getNewResponse(); 222 | 223 | $request->addRawHeaderLine($headerLine); 224 | 225 | $dispatcher->shouldReceive('dispatch') 226 | ->with( 227 | $this->getUrl(), 228 | $this->buildDispatcherOptions( 229 | 'PUT', 230 | [ 231 | $headerLine 232 | ], 233 | [] 234 | ) 235 | ) 236 | ->andReturn($response) 237 | ->once(); 238 | 239 | $request->put($this->getUrl()); 240 | } 241 | 242 | public function testAddRawOption() 243 | { 244 | $dispatcher = $this->getMockOfTheDispatcher(); 245 | $request = $this->getNewRequest($dispatcher, [], []); 246 | $response = $this->getNewResponse(); 247 | 248 | $request->addRawOption('foo', 'bar'); 249 | 250 | $dispatcher->shouldReceive('dispatch') 251 | ->with( 252 | $this->getUrl(), 253 | $this->buildDispatcherOptions( 254 | 'PUT', 255 | [], 256 | [ 257 | 'foo' => 'bar' 258 | ] 259 | ) 260 | ) 261 | ->andReturn($response) 262 | ->once(); 263 | 264 | $request->put($this->getUrl()); 265 | } 266 | 267 | 268 | 269 | /** 270 | * @dataProvider testCaseWithUrlParameters 271 | * @param array $parameters 272 | * @param $url 273 | * @param $expectedUrl 274 | */ 275 | public function testGet(array $parameters, $url, $expectedUrl) 276 | { 277 | $dispatcher = $this->getMockOfTheDispatcher(); 278 | $request = $this->getNewRequest($dispatcher); 279 | $response = $this->getNewResponse(); 280 | 281 | $dispatcher->shouldReceive('dispatch') 282 | ->with( 283 | $expectedUrl, 284 | $this->buildDispatcherOptions('GET') 285 | ) 286 | ->andReturn($response) 287 | ->once(); 288 | 289 | $request->get($url, $parameters); 290 | } 291 | 292 | 293 | 294 | /** 295 | * @dataProvider testCaseWithUrlParameters 296 | * @param array $parameters 297 | * @param $url 298 | * @param $expectedUrl 299 | */ 300 | public function testDelete(array $parameters, $url, $expectedUrl) 301 | { 302 | $dispatcher = $this->getMockOfTheDispatcher(); 303 | $request = $this->getNewRequest($dispatcher); 304 | $response = $this->getNewResponse(); 305 | 306 | $dispatcher->shouldReceive('dispatch') 307 | ->with( 308 | $expectedUrl, 309 | $this->buildDispatcherOptions('DELETE') 310 | ) 311 | ->andReturn($response) 312 | ->once(); 313 | 314 | $request->delete($url, $parameters); 315 | } 316 | 317 | 318 | 319 | /** 320 | * @dataProvider testCaseWithUrlParametersAndData 321 | * @param array $parameters 322 | * @param mixed $data 323 | * @param string $url 324 | * @param string $expectedUrl 325 | * @param mixed $expectedData 326 | */ 327 | public function testPatch(array $parameters, $data, $url, $expectedUrl, $expectedData) 328 | { 329 | $dispatcher = $this->getMockOfTheDispatcher(); 330 | $request = $this->getNewRequest($dispatcher); 331 | $response = $this->getNewResponse(); 332 | 333 | $dispatcher->shouldReceive('dispatch') 334 | ->with( 335 | $expectedUrl, 336 | $this->buildDispatcherOptions( 337 | 'PATCH', 338 | [], 339 | [], 340 | $expectedData 341 | ) 342 | ) 343 | ->andReturn($response) 344 | ->once(); 345 | 346 | $request->patch($url, $parameters, $data); 347 | } 348 | 349 | 350 | 351 | /** 352 | * @dataProvider testCaseWithUrlParametersAndData 353 | * @param array $parameters 354 | * @param mixed $data 355 | * @param string $url 356 | * @param string $expectedUrl 357 | * @param mixed $expectedData 358 | */ 359 | public function testPost(array $parameters, $data, $url, $expectedUrl, $expectedData) 360 | { 361 | $dispatcher = $this->getMockOfTheDispatcher(); 362 | $request = $this->getNewRequest($dispatcher); 363 | $response = $this->getNewResponse(); 364 | 365 | $dispatcher->shouldReceive('dispatch') 366 | ->with( 367 | $expectedUrl, 368 | $this->buildDispatcherOptions( 369 | 'POST', 370 | [], 371 | [], 372 | $expectedData 373 | ) 374 | ) 375 | ->andReturn($response) 376 | ->once(); 377 | 378 | $request->post($url, $parameters, $data); 379 | } 380 | 381 | 382 | 383 | /** 384 | * @dataProvider testCaseWithUrlParametersAndData 385 | * @param array $parameters 386 | * @param mixed $data 387 | * @param string $url 388 | * @param string $expectedUrl 389 | * @param mixed $expectedData 390 | */ 391 | public function testPut(array $parameters, $data, $url, $expectedUrl, $expectedData) 392 | { 393 | $dispatcher = $this->getMockOfTheDispatcher(); 394 | $request = $this->getNewRequest($dispatcher); 395 | $response = $this->getNewResponse(); 396 | 397 | $dispatcher->shouldReceive('dispatch') 398 | ->with( 399 | $expectedUrl, 400 | $this->buildDispatcherOptions( 401 | 'PUT', 402 | [], 403 | [], 404 | $expectedData 405 | ) 406 | ) 407 | ->andReturn($response) 408 | ->once(); 409 | 410 | $request->put($url, $parameters, $data); 411 | } 412 | 413 | public function testRestWithoutResettingTheDefaults() 414 | { 415 | $dispatcher = $this->getMockOfTheDispatcher(); 416 | $request = $this->getNewRequest($dispatcher, [], []); 417 | $response = $this->getNewResponse(); 418 | 419 | $request->addRawOption('foo', 'bar'); 420 | $request->reset(); 421 | 422 | $dispatcher->shouldReceive('dispatch') 423 | ->with( 424 | $this->getUrl(), 425 | $this->buildDispatcherOptions('PUT') 426 | ) 427 | ->andReturn($response) 428 | ->once(); 429 | 430 | $request->put($this->getUrl()); 431 | } 432 | 433 | public function testRestWithResettingTheDefaults() 434 | { 435 | $dispatcher = $this->getMockOfTheDispatcher(); 436 | $request = $this->getNewRequest( 437 | $dispatcher, 438 | [ 439 | 'foo' => 'bar' 440 | ], 441 | [] 442 | ); 443 | $response = $this->getNewResponse(); 444 | 445 | $request->reset(true); 446 | 447 | $dispatcher->shouldReceive('dispatch') 448 | ->with( 449 | $this->getUrl(), 450 | $this->buildDispatcherOptions('PUT') 451 | ) 452 | ->andReturn($response) 453 | ->once(); 454 | 455 | $request->put($this->getUrl()); 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /test/Response/ResponseTest.php: -------------------------------------------------------------------------------- 1 | 4 | * @since: 2015-12-14 5 | */ 6 | 7 | namespace Test\Net\Bazzline\Component\Curl; 8 | 9 | class ResponseTest extends AbstractTestCase 10 | { 11 | public function testHeaderLineWithExistingPrefixes() 12 | { 13 | $content = 'the content'; 14 | $contentType = 'the content type'; 15 | $error = 'this is an error'; 16 | $errorCode = __LINE__; 17 | $headerLines = [ 18 | 'bar' => 'foo', 19 | 'foo' => 'bar' 20 | ]; 21 | $statusCode = __LINE__; 22 | 23 | $response = $this->getNewResponse($content, $contentType, $error, $errorCode, $headerLines, $statusCode); 24 | 25 | foreach ($headerLines as $prefix => $suffix) { 26 | $this->assertEquals($suffix, $response->headerLine($prefix)); 27 | } 28 | } 29 | 30 | /** 31 | * @expectedException \InvalidArgumentException 32 | * @expectedExceptionMessage no headline available for prefix: "foobar" 33 | */ 34 | public function testHeaderLineWithNotExistingPrefixes() 35 | { 36 | $content = 'the content'; 37 | $contentType = 'the content type'; 38 | $error = 'this is an error'; 39 | $errorCode = __LINE__; 40 | $headerLines = []; 41 | $statusCode = __LINE__; 42 | 43 | $response = $this->getNewResponse($content, $contentType, $error, $errorCode, $headerLines, $statusCode); 44 | 45 | $this->assertEquals(null, $response->headerLine('foobar')); 46 | } 47 | 48 | public function testAll() 49 | { 50 | $content = 'the content'; 51 | $contentType = 'the content type'; 52 | $error = 'this is an error'; 53 | $errorCode = __LINE__; 54 | $headerLines = []; 55 | $statusCode = __LINE__; 56 | 57 | $response = $this->getNewResponse($content, $contentType, $error, $errorCode, $headerLines, $statusCode); 58 | 59 | $this->assertEquals($content, $response->content()); 60 | $this->assertEquals($contentType, $response->contentType()); 61 | $this->assertEquals($error, $response->error()); 62 | $this->assertEquals($errorCode, $response->errorCode()); 63 | $this->assertEquals($headerLines, $response->headerLines()); 64 | $this->assertEquals($statusCode, $response->statusCode()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/ResponseBehaviour/ConvertJsonToArrayBehaviourTest.php: -------------------------------------------------------------------------------- 1 | 4 | * @since: 2015-12-16 5 | */ 6 | 7 | namespace Test\Net\Bazzline\Component\Curl\ResponseBehaviour; 8 | 9 | use Net\Bazzline\Component\Curl\ResponseBehaviour\ConvertJsonToArrayBehaviour; 10 | use Test\Net\Bazzline\Component\Curl\AbstractTestCase; 11 | 12 | class ConvertJsonToArrayBehaviourTest extends AbstractTestCase 13 | { 14 | public function testBehaviour() 15 | { 16 | $behaviour = new ConvertJsonToArrayBehaviour(); 17 | $response = $this->getNewResponse(); 18 | 19 | $behavedResponse = $behaviour->behave($response); 20 | $expectedContent = json_decode($response->content(), true); 21 | 22 | $this->assertEquals($expectedContent, $behavedResponse->content()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/ResponseBehaviour/ThrowRuntimeExceptionIfStatusCodeIsAboveTheLimitBehaviourTest.php: -------------------------------------------------------------------------------- 1 | 4 | * @since: 2015-12-16 5 | */ 6 | 7 | namespace Test\Net\Bazzline\Component\Curl\ResponseBehaviour; 8 | 9 | use Net\Bazzline\Component\Curl\Response\Response; 10 | use Net\Bazzline\Component\Curl\ResponseBehaviour\ThrowRuntimeExceptionIfStatusCodeIsAboveTheLimitBehaviour; 11 | use RuntimeException; 12 | use Test\Net\Bazzline\Component\Curl\AbstractTestCase; 13 | 14 | class ThrowRuntimeExceptionIfStatusCodeIsAboveTheLimitBehaviourTest extends AbstractTestCase 15 | { 16 | /** 17 | * @return array 18 | */ 19 | public function testCases() 20 | { 21 | return [ 22 | 'status code below limit' => [ 23 | 0, 24 | 1, 25 | $this->createResponse(0), 26 | false 27 | ], 28 | 'status code on the limit' => [ 29 | 1, 30 | 1, 31 | $this->createResponse(1), 32 | true 33 | ], 34 | 'status code over the limit' => [ 35 | 2, 36 | 1, 37 | $this->createResponse(2), 38 | true 39 | ] 40 | ]; 41 | } 42 | 43 | /** 44 | * @dataProvider testCases 45 | * @param int $statusCode 46 | * @param int $firstStatusCodeThatIsOverTheLimit 47 | * @param Response $response 48 | * @param bool $exceptionExpected 49 | */ 50 | public function testBehaviour($statusCode, $firstStatusCodeThatIsOverTheLimit, Response $response, $exceptionExpected) 51 | { 52 | $behaviour = new ThrowRuntimeExceptionIfStatusCodeIsAboveTheLimitBehaviour($firstStatusCodeThatIsOverTheLimit); 53 | 54 | try { 55 | $behaviour->behave($response); 56 | } catch (RuntimeException $exception) { 57 | if ($exceptionExpected) { 58 | $expectedExceptionMessage = 'limit of status code exceeded. dumping response: ' . implode(', ', $response->convertIntoAnArray()); 59 | $this->assertEquals($exception->getMessage(), $expectedExceptionMessage); 60 | } else { 61 | $this->fail('exception not expected: ' . $exception->getMessage()); 62 | } 63 | } 64 | } 65 | 66 | /** 67 | * @param int $statusCode 68 | * @return Response 69 | */ 70 | private function createResponse($statusCode) 71 | { 72 | return $this->getNewResponse('example content', 'example content type', '', 0, [], $statusCode); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /test/bootstrap.php: -------------------------------------------------------------------------------- 1 |