Last active
June 26, 2020 06:27
-
-
Save ArrayIterator/a305dd657a61d0f6631157dc87f1be32 to your computer and use it in GitHub Desktop.
TinyPng Optimize Image API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace ArrayIterator\App\TinyPng; | |
use Exception; | |
use GuzzleHttp\Client; | |
use GuzzleHttp\Psr7\Stream; | |
use GuzzleHttp\RequestOptions; | |
use InvalidArgumentException; | |
use Psr\Http\Message\StreamInterface; | |
use RuntimeException; | |
// composer dependency | |
/* | |
{ | |
"name" : "name/of-app", | |
"require" : { | |
"php" : "^7.2", | |
"ext-json": "*", | |
"guzzlehttp/guzzle": "^6" | |
} | |
} | |
*/ | |
// require vendor if not loaded | |
// require __DIR__ .'/vendor/autoload.php'; | |
/** | |
* Class TinyPng | |
* @package ArrayIterator\App\TinyPng | |
* $tinyPng = new TinyPng(); | |
* try { | |
* $stream = $tinyPng->optimizeFromFile( | |
* '/path/to/file/image.jpg', | |
* ['timeout' => 60] | |
* ); | |
* } catch(\Exception $e) { | |
* | |
* } | |
*/ | |
class TinyPng | |
{ | |
/** | |
* @var string | |
*/ | |
protected $tinyApiUrl = 'https://tinypng.com/web/shrink'; | |
/** | |
* @var Client | |
*/ | |
protected $client; | |
/** | |
* @var array | |
*/ | |
protected $headers = []; | |
/** | |
* TinyPng constructor. | |
*/ | |
public function __construct() | |
{ | |
$this->headers = [ | |
'Accept' => '*/*', | |
'Accept-encoding' => 'gzip, deflate, br', | |
'Accept-language' => 'en-US,en;q=0.9,id;q=0.8,fr;q=0.7', | |
'Origin' => 'https://tinypng.com', | |
'Referer' => 'https://tinypng.com/', | |
'Authority' => 'tinypng.com', | |
// random USER AGENT | |
'User-Agent' => sprintf( | |
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_1%1$s_%2$s)' | |
. ' AppleWebKit/53%3$s.%4$s (KHTML, like Gecko) Chrome/7%5$s.0.%6$s.%7$s Safari/53%3$s.%4$s', | |
rand(0, 3), | |
rand(0, 6), | |
rand(1, 9), | |
rand(20, 40), | |
rand(0, 4), | |
rand(2010, 3730), | |
rand(101, 131) | |
), | |
]; | |
} | |
/** | |
* @return array | |
*/ | |
public function getHeaders() : array | |
{ | |
return $this->headers; | |
} | |
/** | |
* @param array $headers | |
*/ | |
public function setHeaders(array $headers) | |
{ | |
$this->headers = $headers; | |
} | |
/** | |
* @return string | |
*/ | |
public function getTinyApiUrl() : string | |
{ | |
return $this->tinyApiUrl; | |
} | |
/** | |
* @return Client | |
*/ | |
public function getClient() : Client | |
{ | |
if (!$this->client) { | |
$this->client = new Client([ | |
RequestOptions::HEADERS => $this->headers | |
]); | |
} | |
return $this->client; | |
} | |
/** | |
* @param string $file | |
* @param array $additionalOption | |
* @return string|array|StreamInterface | |
* returning array if result is valid but no output given | |
* returning string content json if result is 200 but no json array | |
* returning StreamInterface when it was valid | |
* @throws InvalidArgumentException|Exception | |
* @see optimizeImage() | |
*/ | |
public function optimizeFromFile( | |
string $file, | |
array $additionalOption = [ | |
RequestOptions::TIMEOUT => 30 | |
] | |
) { | |
if (!$socket = @fopen($file, 'r')) { | |
throw new RuntimeException( | |
sprintf('Failed to open %s', $file) | |
); | |
} | |
$stream = new Stream($socket); | |
$response = $this->optimizeFromStream($stream, $additionalOption); | |
$stream->close(); | |
return $response; | |
} | |
/** | |
* @param StreamInterface $stream | |
* @param array $additionalOption | |
* @return string|array|StreamInterface | |
* returning array if result is valid but no output given | |
* returning string content json if result is 200 but no json array | |
* returning StreamInterface when it was valid | |
* @throws InvalidArgumentException|Exception | |
* | |
*/ | |
public function optimizeFromStream( | |
StreamInterface $stream, | |
array $additionalOption = [ | |
RequestOptions::TIMEOUT => 30 | |
] | |
) { | |
$stream->rewind(); | |
if (!@getimagesizefromstring($stream->getContents())) { | |
throw new InvalidArgumentException( | |
'Stream does not contain valid image file', | |
E_USER_WARNING | |
); | |
} | |
// rewind | |
$stream->rewind(); | |
$opt = array_merge( | |
[ | |
RequestOptions::VERIFY => false, | |
RequestOptions::FORCE_IP_RESOLVE => 'ipv4', | |
], | |
$additionalOption | |
); | |
$opt[RequestOptions::BODY] = $stream; | |
$headers = $opt[RequestOptions::HEADERS]??[]; | |
if (!is_array($headers)) { | |
$headers = []; | |
} | |
foreach ($headers as $key => $v) { | |
if (!is_string($key) // if key is not headers | |
// if value is not string, numeric or boolean | |
// unset here | |
|| (!is_string($v) && !is_numeric($key) && !is_bool($v)) | |
) { | |
unset($headers[$key]); | |
} | |
} | |
$headers = array_merge($this->getHeaders(), $headers); | |
// override headers for right way | |
$headers['Origin'] = 'https://tinypng.com'; | |
$headers['Referer'] = 'https://tinypng.com/'; | |
$headers['Authority'] = 'tinypng.com'; | |
$opt[RequestOptions::HEADERS] = $headers; | |
$stream->rewind(); | |
try { | |
$response = $this->getClient()->post($this->getTinyApiUrl(), $opt); | |
$body = (string)$response->getBody(); | |
$response->getBody()->close(); | |
unset($response); | |
$json = json_decode($body, true); | |
if (is_array($json)) { | |
if (isset($json['output']) | |
&& is_array($json['output']) | |
&& isset($json['output']['url']) | |
&& is_string($json['output']['url']) | |
) { | |
try { | |
$client = $this->getClient()->get( | |
$json['output']['url'], | |
[ | |
RequestOptions::HEADERS => $headers | |
] | |
); | |
return $client->getBody(); | |
} catch (Exception $e) { | |
throw $e; | |
} | |
} | |
// if arrays but no output | |
return $json; | |
} | |
return $body; | |
} catch (Exception $e) { | |
throw $e; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment