diff --git a/README.md b/README.md index 64af346..803e3f5 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,13 @@ Example using PHP.Gt/Curl: -------------------------- ```php -$curl = new Curl("https://circleci.com/api/v1.1/project/github/PhpGt/Dom"); +$curl = new Curl("https://catfact.ninja/fact"); $curl->exec(); $json = $curl->outputJson(); -echo "Latest build status: " . $json[0]->status; +echo "Here's a cat fact: {$json->getString("fact")}"; +echo PHP_EOL; +echo "The fact's length is {$json->getInt("length")} characters."; +echo PHP_EOL; ``` Same example using PHP's native `curl_*` functions: @@ -43,7 +46,7 @@ Same example using PHP's native `curl_*` functions: ```php // Using native functionality to achieve the same: $ch = curl_init(); -curl_setopt($ch, CURLOPT_URL, "https://circleci.com/api/v1.1/project/github/PhpGt/Dom"); +curl_setopt($ch, CURLOPT_URL, "https://catfact.ninja/fact"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); if(false === $result) { @@ -53,5 +56,10 @@ $json = json_decode($result); if(is_null($json)) { die("JSON decoding error: " . json_last_error_msg()); } -echo "Latest build status: " . $json[0]->status; -``` + +// Note: No type checks are made on the `fact` and `length` properties here. +echo "Here's a cat fact: {$json->fact}"; +echo PHP_EOL; +echo "The fact's length is {$json->length} characters."; +echo PHP_EOL; +``` diff --git a/composer.json b/composer.json index 1ec42f8..42d39c0 100644 --- a/composer.json +++ b/composer.json @@ -4,9 +4,10 @@ "type": "library", "require": { - "php": ">=7.2.0", + "php": ">=7.4", "ext-curl": "*", - "ext-json": "*" + "ext-json": "*", + "phpgt/json": "^v1.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index b25a3ea..73ec5df 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,163 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d687a152d8e55cc48a0d4ce14ab28d86", - "packages": [], + "content-hash": "1ffd20c200575237f23b625e4221a1b3", + "packages": [ + { + "name": "phpgt/dataobject", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/DataObject.git", + "reference": "02e08144a0ed4b2bf022baf125803dfbbfc1b487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/DataObject/zipball/02e08144a0ed4b2bf022baf125803dfbbfc1b487", + "reference": "02e08144a0ed4b2bf022baf125803dfbbfc1b487", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.0", + "phpgt/typesafegetter": "^1.0" + }, + "require-dev": { + "phpstan/phpstan": ">=0.12.64", + "phpunit/phpunit": "9.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\DataObject\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com" + } + ], + "description": " Structured, type-safe, immutable data transfer.", + "support": { + "issues": "https://github.com/PhpGt/DataObject/issues", + "source": "https://github.com/PhpGt/DataObject/tree/v1.0.0" + }, + "funding": [ + { + "url": "https://github.com/phpgt", + "type": "github" + } + ], + "time": "2021-03-15T18:03:44+00:00" + }, + { + "name": "phpgt/json", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/Json.git", + "reference": "2bad75c37b85e3b0c30cd7c7325305c83b1b9023" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/Json/zipball/2bad75c37b85e3b0c30cd7c7325305c83b1b9023", + "reference": "2bad75c37b85e3b0c30cd7c7325305c83b1b9023", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.0", + "phpgt/dataobject": "^1.0" + }, + "require-dev": { + "phpstan/phpstan": "0.12.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\Json\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com" + } + ], + "description": " Structured, type-safe, immutable JSON objects.", + "support": { + "issues": "https://github.com/PhpGt/Json/issues", + "source": "https://github.com/PhpGt/Json/tree/v1.0.0" + }, + "funding": [ + { + "url": "https://github.com/phpgt", + "type": "github" + } + ], + "time": "2021-03-15T18:13:19+00:00" + }, + { + "name": "phpgt/typesafegetter", + "version": "v1.2.4", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/TypeSafeGetter.git", + "reference": "ebffd758e69b8a0eebcad30f3daf408915b9ddf3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/TypeSafeGetter/zipball/ebffd758e69b8a0eebcad30f3daf408915b9ddf3", + "reference": "ebffd758e69b8a0eebcad30f3daf408915b9ddf3", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpstan/phpstan": "v1.8.0", + "phpunit/phpunit": "v9.5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\TypeSafeGetter\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com" + } + ], + "description": "An interface for objects that expose type-safe getter methods.", + "support": { + "issues": "https://github.com/PhpGt/TypeSafeGetter/issues", + "source": "https://github.com/PhpGt/TypeSafeGetter/tree/v1.2.4" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2022-07-08T17:17:42+00:00" + } + ], "packages-dev": [ { "name": "doctrine/instantiator", @@ -1799,7 +1954,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.2.0", + "php": ">=7.4", "ext-curl": "*", "ext-json": "*" }, diff --git a/examples/01-json-usage.php b/examples/01-json-usage.php new file mode 100644 index 0000000..9bc31a0 --- /dev/null +++ b/examples/01-json-usage.php @@ -0,0 +1,21 @@ +exec(); +$json = $curl->outputJson(); +echo "Here's a cat fact: {$json->getString("fact")}"; +echo PHP_EOL; +echo "The fact's length is {$json->getInt("length")} characters."; +echo PHP_EOL; + +/* Example output: +Here's a cat fact: Phoenician cargo ships are thought to have brought the first domesticated cats to Europe in about 900 BC. +The fact's length is 105 characters. +*/ diff --git a/examples/02-curl-multi.php b/examples/02-curl-multi.php new file mode 100644 index 0000000..64ec271 --- /dev/null +++ b/examples/02-curl-multi.php @@ -0,0 +1,36 @@ +setOpt(CURLOPT_RETURNTRANSFER, true); +$curlIp = new Curl("https://api.ipify.org/?format=json"); +$curlIp->setOpt(CURLOPT_RETURNTRANSFER, true); +$curlTimeout = new Curl("https://this-domain-name-does-not-exist.example.com/nothing.json"); +$curlTimeout->setOpt(CURLOPT_RETURNTRANSFER, true); + +$multi = new CurlMulti(); +$multi->add($curlCat); +$multi->add($curlIp); +$multi->add($curlTimeout); + +$stillRunning = 0; +do { + $multi->exec($stillRunning); + usleep(100_000); + echo "."; +} +while($stillRunning > 0); + +echo PHP_EOL; +echo "Cat API response: " . $multi->getContent($curlCat) . PHP_EOL; +echo "IP API response: " . $multi->getContent($curlIp) . PHP_EOL; +echo "Timeout API response: " . $multi->getContent($curlTimeout) . PHP_EOL; diff --git a/examples/03-curl-multi-streaming.php b/examples/03-curl-multi-streaming.php new file mode 100644 index 0000000..64c511c --- /dev/null +++ b/examples/03-curl-multi-streaming.php @@ -0,0 +1,48 @@ +setOpt(CURLOPT_HEADERFUNCTION, function ($ch, string $rawHeader):int { + echo "HEADER: $rawHeader"; + return strlen($rawHeader); + }); + $curl->setOpt(CURLOPT_WRITEFUNCTION, function ($ch, string $rawBody):int { + echo "BODY: $rawBody\n"; + return strlen($rawBody); + }); + $multi->add($curl); +} + +$stillRunning = 0; +do { + $multi->exec($stillRunning); + usleep(10_000); + echo "."; +} +while($stillRunning > 0); + +echo PHP_EOL; diff --git a/src/Curl.php b/src/Curl.php index 887fb85..170b5c5 100644 --- a/src/Curl.php +++ b/src/Curl.php @@ -1,11 +1,13 @@ init($url); @@ -73,19 +75,9 @@ public function output():string { public function outputJson( int $depth = 512, int $options = 0 - ) { - $json = json_decode( - $this->output(), - false, - $depth, - $options - ); - if(is_null($json)) { - $errorMessage = json_last_error_msg(); - throw new JsonDecodeException($errorMessage); - } - - return $json; + ):JsonObject { + $builder = new JsonObjectBuilder(); + return $builder->fromJsonString($this->output()); } /** @@ -128,7 +120,7 @@ public function exec():string { if(true === $response) { $response = $this->buffer; } - + return $response; } @@ -207,6 +199,9 @@ public function getHandle(){ * Gets all CURLINFO_ data, identical to calling curl_getinfo with no arguments. */ public function getAllInfo():array { - return curl_getinfo($this->ch, 0); + $result = curl_getinfo($this->ch, 0); + if(!$result) { + return []; + } } -} \ No newline at end of file +} diff --git a/src/CurlMulti.php b/src/CurlMulti.php index 0029be5..c89440e 100644 --- a/src/CurlMulti.php +++ b/src/CurlMulti.php @@ -26,6 +26,7 @@ public static function strerror(int $errorNum):string { * @throws CurlException if a CURLM_XXX error is made */ public function add(CurlInterface $curl):void { +// $curl->setOpt(CURLOPT_RETURNTRANSFER, true); curl_multi_add_handle($this->mh, $curl->getHandle()); } @@ -68,7 +69,7 @@ public function getHandle() { * @return string the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */ public function getContent(CurlInterface $curl):string { - return curl_multi_getcontent($curl->getHandle()); + return curl_multi_getcontent($curl->getHandle()) ?? ""; } /** @@ -125,4 +126,4 @@ public function select(float $timeout = 1.0):int { public function setOpt(int $option, $value):void { curl_multi_setopt($this->mh, $option, $value); } -} \ No newline at end of file +} diff --git a/src/CurlObjectLookup.php b/src/CurlObjectLookup.php index d992edc..de913c2 100644 --- a/src/CurlObjectLookup.php +++ b/src/CurlObjectLookup.php @@ -2,8 +2,8 @@ namespace Gt\Curl; class CurlObjectLookup { - protected static $objectMap = []; - protected static $resourceMap = []; + protected static array $objectMap = []; + protected static array $resourceMap = []; public static function add(CurlInterface $curl):void { self::$objectMap []= $curl; @@ -19,4 +19,4 @@ public static function getObjectFromHandle($ch):?CurlInterface { return null; } -} \ No newline at end of file +}