Skip to content

Instantly share code, notes, and snippets.

@ArrayIterator
Last active June 26, 2020 06:27
Show Gist options
  • Save ArrayIterator/a305dd657a61d0f6631157dc87f1be32 to your computer and use it in GitHub Desktop.
Save ArrayIterator/a305dd657a61d0f6631157dc87f1be32 to your computer and use it in GitHub Desktop.
TinyPng Optimize Image API
<?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