forked from rebillar/site-accueil-insa
246 lines
5.5 KiB
PHP
246 lines
5.5 KiB
PHP
<?php
|
|
|
|
namespace Davaxi;
|
|
|
|
use Davaxi\Sparkline\DataTrait;
|
|
use Davaxi\Sparkline\FormatTrait;
|
|
use Davaxi\Sparkline\Picture;
|
|
use Davaxi\Sparkline\PointTrait;
|
|
use Davaxi\Sparkline\StyleTrait;
|
|
use InvalidArgumentException;
|
|
|
|
/**
|
|
* Class Sparkline.
|
|
*/
|
|
class Sparkline
|
|
{
|
|
use StyleTrait;
|
|
use DataTrait;
|
|
use FormatTrait;
|
|
use PointTrait;
|
|
|
|
const MIN_DATA_LENGTH = 2;
|
|
const FORMAT_DIMENSION = 2;
|
|
const HEXADECIMAL_ALIAS_LENGTH = 3;
|
|
const CSS_PADDING_ONE = 1;
|
|
const CSS_PADDING_TWO = 2;
|
|
const CSS_PADDING_THREE = 3;
|
|
const CSS_PADDING = 4;
|
|
|
|
/**
|
|
* @var string
|
|
* ex: QUERY_STRING if dedicated url
|
|
*/
|
|
protected $eTag;
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
protected $expire;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected $filename = 'sparkline';
|
|
|
|
/**
|
|
* @var resource
|
|
*/
|
|
protected $file;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $server = [];
|
|
|
|
/**
|
|
* Sparkline constructor.
|
|
*
|
|
* @codeCoverageIgnore
|
|
*/
|
|
public function __construct()
|
|
{
|
|
if (!extension_loaded('gd')) {
|
|
throw new InvalidArgumentException('GD extension is not installed');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string|null $eTag
|
|
*/
|
|
public function setETag($eTag)
|
|
{
|
|
if (null === $eTag) {
|
|
$this->eTag = null;
|
|
|
|
return;
|
|
}
|
|
$this->eTag = md5($eTag);
|
|
}
|
|
|
|
/**
|
|
* @param string $filename
|
|
* Without extension
|
|
*/
|
|
public function setFilename(string $filename)
|
|
{
|
|
$this->filename = $filename;
|
|
}
|
|
|
|
/**
|
|
* @param string|int $expire
|
|
* time format or string format
|
|
*/
|
|
public function setExpire($expire)
|
|
{
|
|
if (null === $expire) {
|
|
$this->expire = null;
|
|
|
|
return;
|
|
}
|
|
if (is_numeric($expire)) {
|
|
$this->expire = $expire;
|
|
|
|
return;
|
|
}
|
|
$this->expire = strtotime($expire);
|
|
}
|
|
|
|
public function generate()
|
|
{
|
|
list($width, $height) = $this->getNormalizedSize();
|
|
|
|
$count = $this->getCount();
|
|
|
|
$picture = new Picture($width, $height);
|
|
$picture->applyBackground($this->backgroundColor);
|
|
|
|
$lineThickness = (int)round($this->lineThickness * $this->ratioComputing);
|
|
$picture->applyThickness($lineThickness);
|
|
|
|
$stepCount = $this->getMaxNumberOfDataPointsAcrossSerieses();
|
|
|
|
foreach ($this->data as $seriesIndex => $series) {
|
|
$seriesNormalized = $this->getNormalizedData($seriesIndex);
|
|
list($polygon, $line) = $this->getChartElements($seriesNormalized, $stepCount);
|
|
$picture->applyPolygon($polygon, $this->getFillColor($seriesIndex), $count);
|
|
$picture->applyLine($line, $this->getLineColor($seriesIndex));
|
|
|
|
foreach ($this->points as $point) {
|
|
if ($point['series'] != $seriesIndex) {
|
|
continue;
|
|
}
|
|
|
|
$isFirst = $point['index'] === 0;
|
|
$lineIndex = $isFirst ? 0 : $point['index'] - 1;
|
|
$picture->applyDot(
|
|
$line[$lineIndex][$isFirst ? 0 : 2],
|
|
$line[$lineIndex][$isFirst ? 1 : 3],
|
|
$point['radius'] * $this->ratioComputing,
|
|
$point['color']
|
|
);
|
|
}
|
|
}
|
|
|
|
$this->file = $picture->generate($this->width, $this->height);
|
|
}
|
|
|
|
/**
|
|
* @param array $server
|
|
*/
|
|
public function setServer(array $server)
|
|
{
|
|
$this->server = $server;
|
|
}
|
|
|
|
/**
|
|
* @param string $key
|
|
*
|
|
* @return mixed|null
|
|
*/
|
|
public function getServerValue(string $key)
|
|
{
|
|
if (isset($this->server[$key])) {
|
|
return $this->server[$key];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
protected function checkNoModified(): bool
|
|
{
|
|
$httpIfNoneMatch = $this->getServerValue('HTTP_IF_NONE_MATCH');
|
|
if ($this->eTag && $httpIfNoneMatch) {
|
|
if ($httpIfNoneMatch === $this->eTag) {
|
|
$serverProtocol = $this->getServerValue('SERVER_PROTOCOL');
|
|
header($serverProtocol . ' 304 Not Modified', true, 304);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function display()
|
|
{
|
|
if (!$this->file) {
|
|
$this->generate();
|
|
}
|
|
|
|
if ($this->checkNoModified()) {
|
|
return;
|
|
}
|
|
|
|
header('Content-Type: image/png');
|
|
header('Content-Disposition: inline; filename="' . $this->filename . '.png"');
|
|
header('Accept-Ranges: none');
|
|
if ($this->eTag) {
|
|
header('ETag: ' . $this->eTag);
|
|
}
|
|
if (null !== $this->expire) {
|
|
header('Expires: ' . gmdate('D, d M Y H:i:s T', $this->expire));
|
|
}
|
|
imagepng($this->file);
|
|
}
|
|
|
|
/**
|
|
* @param string $savePath
|
|
*/
|
|
public function save(string $savePath)
|
|
{
|
|
if (!$this->file) {
|
|
$this->generate();
|
|
}
|
|
imagepng($this->file, $savePath);
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function toBase64(): string
|
|
{
|
|
if (!$this->file) {
|
|
$this->generate();
|
|
}
|
|
ob_start();
|
|
imagepng($this->file);
|
|
$buffer = ob_get_contents();
|
|
if (ob_get_length()) {
|
|
ob_end_clean();
|
|
}
|
|
|
|
return base64_encode($buffer);
|
|
}
|
|
|
|
public function destroy()
|
|
{
|
|
if ($this->file) {
|
|
imagedestroy($this->file);
|
|
}
|
|
$this->file = null;
|
|
}
|
|
}
|