From c5d8696eb74e4d1a6e4e64ffe3ab90aa6f6205f9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 13:46:58 +0000 Subject: [PATCH 1/5] Initial plan From fe9d1462b9c20ff3ab0d0f14b6fb6e60b949ae30 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 13:56:48 +0000 Subject: [PATCH 2/5] Add comprehensive PHPDoc comments for static analysis support Co-authored-by: rkoopmans <1124992+rkoopmans@users.noreply.github.com> --- lib/Tinify.php | 75 ++++++++++++++++++++++++++++++++++++++ lib/Tinify/Client.php | 27 ++++++++++++++ lib/Tinify/Exception.php | 11 ++++++ lib/Tinify/Result.php | 23 ++++++++++++ lib/Tinify/ResultMeta.php | 15 ++++++++ lib/Tinify/Source.php | 77 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 228 insertions(+) diff --git a/lib/Tinify.php b/lib/Tinify.php index 80f533f..2dd9fe8 100644 --- a/lib/Tinify.php +++ b/lib/Tinify.php @@ -12,29 +12,52 @@ class Tinify { private static $compressionCount = NULL; private static $client = NULL; + /** + * @param string $key + * @return void + */ public static function setKey($key) { self::$key = $key; self::$client = NULL; } + /** + * @param string $appIdentifier + * @return void + */ public static function setAppIdentifier($appIdentifier) { self::$appIdentifier = $appIdentifier; self::$client = NULL; } + /** + * @param string $proxy + * @return void + */ public static function setProxy($proxy) { self::$proxy = $proxy; self::$client = NULL; } + /** + * @return int|null + */ public static function getCompressionCount() { return self::$compressionCount; } + /** + * @param int $compressionCount + * @return void + */ public static function setCompressionCount($compressionCount) { self::$compressionCount = $compressionCount; } + /** + * @return Client + * @throws AccountException + */ public static function getClient() { if (!self::$key) { throw new AccountException("Provide an API key with Tinify\setKey(...)"); @@ -47,43 +70,95 @@ public static function getClient() { return self::$client; } + /** + * @param Client $client + * @return void + */ public static function setClient($client) { self::$client = $client; } } +/** + * @param string $key + * @return void + */ function setKey($key) { return Tinify::setKey($key); } +/** + * @param string $appIdentifier + * @return void + */ function setAppIdentifier($appIdentifier) { return Tinify::setAppIdentifier($appIdentifier); } +/** + * @param string $proxy + * @return void + */ function setProxy($proxy) { return Tinify::setProxy($proxy); } +/** + * @return int|null + */ function getCompressionCount() { return Tinify::getCompressionCount(); } +/** + * @return int|null + */ function compressionCount() { return Tinify::getCompressionCount(); } +/** + * @param string $path + * @return Source + * @throws AccountException + * @throws ClientException + * @throws ServerException + * @throws ConnectionException + */ function fromFile($path) { return Source::fromFile($path); } +/** + * @param string $string + * @return Source + * @throws AccountException + * @throws ClientException + * @throws ServerException + * @throws ConnectionException + */ function fromBuffer($string) { return Source::fromBuffer($string); } +/** + * @param string $string + * @return Source + * @throws AccountException + * @throws ClientException + * @throws ServerException + * @throws ConnectionException + */ function fromUrl($string) { return Source::fromUrl($string); } +/** + * @return bool + * @throws AccountException + * @throws ServerException + * @throws ConnectionException + */ function validate() { try { Tinify::getClient()->request("post", "/shrink"); diff --git a/lib/Tinify/Client.php b/lib/Tinify/Client.php index 0d8ed0c..05de660 100644 --- a/lib/Tinify/Client.php +++ b/lib/Tinify/Client.php @@ -10,15 +10,28 @@ class Client { private $options; + /** + * @return string + */ public static function userAgent() { $curl = curl_version(); return "Tinify/" . VERSION . " PHP/" . PHP_VERSION . " curl/" . $curl["version"]; } + /** + * @return string + */ private static function caBundle() { return __DIR__ . "/../data/cacert.pem"; } + /** + * @param string $key + * @param string|null $app_identifier + * @param string|null $proxy + * @throws ClientException + * @throws ConnectionException + */ function __construct($key, $app_identifier = NULL, $proxy = NULL) { $curl = curl_version(); @@ -70,6 +83,16 @@ function __construct($key, $app_identifier = NULL, $proxy = NULL) { } } + /** + * @param string $method + * @param string $url + * @param string|array|null $body + * @return object{body: string, headers: array} + * @throws AccountException + * @throws ClientException + * @throws ServerException + * @throws ConnectionException + */ function request($method, $url, $body = NULL) { $header = array(); if (is_array($body)) { @@ -155,6 +178,10 @@ function request($method, $url, $body = NULL) { } } + /** + * @param string|array $headers + * @return array + */ protected static function parseHeaders($headers) { if (!is_array($headers)) { $headers = explode("\r\n", $headers); diff --git a/lib/Tinify/Exception.php b/lib/Tinify/Exception.php index d88bdef..c4595e7 100644 --- a/lib/Tinify/Exception.php +++ b/lib/Tinify/Exception.php @@ -5,6 +5,12 @@ class Exception extends \Exception { public $status; + /** + * @param string $message + * @param string $type + * @param int $status + * @return Exception|AccountException|ClientException|ServerException + */ public static function create($message, $type, $status) { if ($status == 401 || $status == 429) { $klass = "Tinify\AccountException"; @@ -20,6 +26,11 @@ public static function create($message, $type, $status) { return new $klass($message, $type, $status); } + /** + * @param string $message + * @param string|null $type + * @param int|null $status + */ function __construct($message, $type = NULL, $status = NULL) { $this->status = $status; if ($status) { diff --git a/lib/Tinify/Result.php b/lib/Tinify/Result.php index 86ce892..d403ff1 100644 --- a/lib/Tinify/Result.php +++ b/lib/Tinify/Result.php @@ -5,31 +5,54 @@ class Result extends ResultMeta { protected $data; + /** + * @param array $meta + * @param string $data + */ public function __construct($meta, $data) { $this->meta = $meta; $this->data = $data; } + /** + * @return string + */ public function data() { return $this->data; } + /** + * @return string + */ public function toBuffer() { return $this->data; } + /** + * @param string $path + * @return int|false + */ public function toFile($path) { return file_put_contents($path, $this->toBuffer()); } + /** + * @return int + */ public function size() { return intval($this->meta["content-length"]); } + /** + * @return string + */ public function mediaType() { return $this->meta["content-type"]; } + /** + * @return string + */ public function contentType() { return $this->mediaType(); } diff --git a/lib/Tinify/ResultMeta.php b/lib/Tinify/ResultMeta.php index 27aba0e..eae3887 100644 --- a/lib/Tinify/ResultMeta.php +++ b/lib/Tinify/ResultMeta.php @@ -5,22 +5,37 @@ class ResultMeta { protected $meta; + /** + * @param array $meta + */ public function __construct($meta) { $this->meta = $meta; } + /** + * @return int + */ public function width() { return intval($this->meta["image-width"]); } + /** + * @return int + */ public function height() { return intval($this->meta["image-height"]); } + /** + * @return string|null + */ public function location() { return isset($this->meta["location"]) ? $this->meta["location"] : null; } + /** + * @return string|null + */ public function extension() { if (isset($this->meta["content-type"])) { $parts = explode("/", $this->meta["content-type"]); diff --git a/lib/Tinify/Source.php b/lib/Tinify/Source.php index ef7c6a1..9191c10 100644 --- a/lib/Tinify/Source.php +++ b/lib/Tinify/Source.php @@ -5,53 +5,111 @@ class Source { private $url, $commands; + /** + * @param string $path + * @return Source + * @throws AccountException + * @throws ClientException + * @throws ServerException + * @throws ConnectionException + */ public static function fromFile($path) { return self::fromBuffer(file_get_contents($path)); } + /** + * @param string $string + * @return Source + * @throws AccountException + * @throws ClientException + * @throws ServerException + * @throws ConnectionException + */ public static function fromBuffer($string) { $response = Tinify::getClient()->request("post", "/shrink", $string); return new self($response->headers["location"]); } + /** + * @param string $url + * @return Source + * @throws AccountException + * @throws ClientException + * @throws ServerException + * @throws ConnectionException + */ public static function fromUrl($url) { $body = array("source" => array("url" => $url)); $response = Tinify::getClient()->request("post", "/shrink", $body); return new self($response->headers["location"]); } + /** + * @param string $url + * @param array $commands + */ public function __construct($url, $commands = array()) { $this->url = $url; $this->commands = $commands; } + /** + * @return Source + */ public function preserve() { $options = $this->flatten(func_get_args()); $commands = array_merge($this->commands, array("preserve" => $options)); return new self($this->url, $commands); } + /** + * @param array $options + * @return Source + */ public function resize($options) { $commands = array_merge($this->commands, array("resize" => $options)); return new self($this->url, $commands); } + /** + * @param array $options + * @return Result + * @throws AccountException + * @throws ClientException + * @throws ServerException + * @throws ConnectionException + */ public function store($options) { $response = Tinify::getClient()->request("post", $this->url, array_merge($this->commands, array("store" => $options))); return new Result($response->headers, $response->body); } + /** + * @param array $options + * @return Source + */ public function convert($options) { $commands = array_merge($this->commands, array("convert" => $options)); return new self($this->url, $commands); } + /** + * @param array $options + * @return Source + */ public function transform($options) { $commands = array_merge($this->commands, array("transform" => $options)); return new self($this->url, $commands); } + /** + * @return Result + * @throws AccountException + * @throws ClientException + * @throws ServerException + * @throws ConnectionException + */ public function result() { $has_commands = !empty($this->commands); $method = $has_commands ? "post" : "get"; @@ -60,14 +118,33 @@ public function result() { return new Result($response->headers, $response->body); } + /** + * @param string $path + * @return int|false + * @throws AccountException + * @throws ClientException + * @throws ServerException + * @throws ConnectionException + */ public function toFile($path) { return $this->result()->toFile($path); } + /** + * @return string + * @throws AccountException + * @throws ClientException + * @throws ServerException + * @throws ConnectionException + */ public function toBuffer() { return $this->result()->toBuffer(); } + /** + * @param array $options + * @return array + */ private static function flatten($options) { $flattened = array(); foreach ($options as $option) { From 91d9c5fbbbcf74e202101c9ba34ef9bcdc72dd1b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 13:57:53 +0000 Subject: [PATCH 3/5] Use standard PHPDoc return type for Client::request() Co-authored-by: rkoopmans <1124992+rkoopmans@users.noreply.github.com> --- lib/Tinify/Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Tinify/Client.php b/lib/Tinify/Client.php index 05de660..151e500 100644 --- a/lib/Tinify/Client.php +++ b/lib/Tinify/Client.php @@ -87,7 +87,7 @@ function __construct($key, $app_identifier = NULL, $proxy = NULL) { * @param string $method * @param string $url * @param string|array|null $body - * @return object{body: string, headers: array} + * @return \stdClass Object with 'body' (string) and 'headers' (array) properties * @throws AccountException * @throws ClientException * @throws ServerException From 10db5fa5937355338a98d274f918dc467a9d6ce8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 13:58:58 +0000 Subject: [PATCH 4/5] Clarify PHPDoc for flatten method parameter Co-authored-by: rkoopmans <1124992+rkoopmans@users.noreply.github.com> --- lib/Tinify/Source.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Tinify/Source.php b/lib/Tinify/Source.php index 9191c10..1f52e23 100644 --- a/lib/Tinify/Source.php +++ b/lib/Tinify/Source.php @@ -142,8 +142,8 @@ public function toBuffer() { } /** - * @param array $options - * @return array + * @param array $options Array of options from func_get_args() + * @return array Flattened array */ private static function flatten($options) { $flattened = array(); From 7334c541ac3bddf4c7f1a58c18130528cf4cd020 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 14:14:03 +0000 Subject: [PATCH 5/5] Add PHPStan for automated type checking in CI/CD Co-authored-by: rkoopmans <1124992+rkoopmans@users.noreply.github.com> --- .github/workflows/ci-cd.yaml | 13 +++++++++++++ composer.json | 3 ++- phpstan.neon | 5 +++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 phpstan.neon diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index 214caa1..fd2d75d 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -31,6 +31,19 @@ jobs: run: composer install --no-ansi --no-interaction --no-progress - name: Run test suite run: vendor/bin/phpunit + PHPStan: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: shivammathur/setup-php@2.32.0 + with: + php-version: '8.1' + tools: composer:v2 + - uses: actions/checkout@v3 + - name: Install Dependencies + run: composer install --no-ansi --no-interaction --no-progress + - name: Run PHPStan + run: vendor/bin/phpstan analyse Integration_tests: if: github.event_name == 'push' runs-on: ${{ matrix.os }} diff --git a/composer.json b/composer.json index b9b32cc..b4f4c6f 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,8 @@ }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4 || ^9.3" + "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4 || ^9.3", + "phpstan/phpstan": "^1.0" }, "autoload": { diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..664ca65 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,5 @@ +parameters: + level: max + paths: + - lib + phpVersion: 50300