forked from rebillar/site-accueil-insa
392 lines
11 KiB
PHP
392 lines
11 KiB
PHP
<?php
|
|
|
|
namespace CpChart;
|
|
|
|
/**
|
|
* Cache - speed up the rendering by caching up the pictures
|
|
*
|
|
* Version : 2.1.4
|
|
* Made by : Jean-Damien POGOLOTTI
|
|
* Last Update : 19/01/2014
|
|
*
|
|
* This file can be distributed under the license you can find at :
|
|
*
|
|
* http://www.pchart.net/license
|
|
*
|
|
* You can find the whole class documentation on the pChart web site.
|
|
*/
|
|
class Cache
|
|
{
|
|
/**
|
|
* @var string
|
|
*/
|
|
public $CacheFolder;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
public $CacheIndex;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
public $CacheDB;
|
|
|
|
/**
|
|
* @param array $Settings
|
|
*/
|
|
public function __construct(array $Settings = [])
|
|
{
|
|
$CacheFolder = isset($Settings["CacheFolder"]) ? $Settings["CacheFolder"] : __DIR__ . "/../cache";
|
|
$CacheIndex = isset($Settings["CacheIndex"]) ? $Settings["CacheIndex"] : "index.db";
|
|
$CacheDB = isset($Settings["CacheDB"]) ? $Settings["CacheDB"] : "cache.db";
|
|
|
|
$this->CacheFolder = $CacheFolder;
|
|
$this->CacheIndex = $CacheIndex;
|
|
$this->CacheDB = $CacheDB;
|
|
if (!file_exists($this->CacheFolder . "/" . $this->CacheIndex)) {
|
|
touch($this->CacheFolder . "/" . $this->CacheIndex);
|
|
}
|
|
if (!file_exists($this->CacheFolder . "/" . $this->CacheDB)) {
|
|
touch($this->CacheFolder . "/" . $this->CacheDB);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Flush the cache contents
|
|
*/
|
|
public function flush()
|
|
{
|
|
if (file_exists($this->CacheFolder . "/" . $this->CacheIndex)) {
|
|
unlink($this->CacheFolder . "/" . $this->CacheIndex);
|
|
touch($this->CacheFolder . "/" . $this->CacheIndex);
|
|
}
|
|
if (file_exists($this->CacheFolder . "/" . $this->CacheDB)) {
|
|
unlink($this->CacheFolder . "/" . $this->CacheDB);
|
|
touch($this->CacheFolder . "/" . $this->CacheDB);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the MD5 of the data array to clearly identify the chart
|
|
*
|
|
* @param Data $Data
|
|
* @param string $Marker
|
|
* @return string
|
|
*/
|
|
public function getHash(Data $Data, $Marker = "")
|
|
{
|
|
return md5($Marker . serialize($Data->Data));
|
|
}
|
|
|
|
/**
|
|
* Write the generated picture to the cache
|
|
*
|
|
* @param string $ID
|
|
* @param Image $pChartObject
|
|
*/
|
|
public function writeToCache($ID, Image $pChartObject)
|
|
{
|
|
/* Compute the paths */
|
|
$TemporaryFile = tempnam($this->CacheFolder, "tmp_");
|
|
$Database = $this->CacheFolder . "/" . $this->CacheDB;
|
|
$Index = $this->CacheFolder . "/" . $this->CacheIndex;
|
|
/* Flush the picture to a temporary file */
|
|
imagepng($pChartObject->Picture, $TemporaryFile);
|
|
|
|
/* Retrieve the files size */
|
|
$PictureSize = filesize($TemporaryFile);
|
|
$DBSize = filesize($Database);
|
|
|
|
/* Save the index */
|
|
$Handle = fopen($Index, "a");
|
|
fwrite($Handle, $ID . "," . $DBSize . "," . $PictureSize . "," . time() . ",0\r\n");
|
|
fclose($Handle);
|
|
|
|
/* Get the picture raw contents */
|
|
$Handle = fopen($TemporaryFile, "r");
|
|
$Raw = fread($Handle, $PictureSize);
|
|
fclose($Handle);
|
|
|
|
/* Save the picture in the solid database file */
|
|
$Handle = fopen($Database, "a");
|
|
fwrite($Handle, $Raw);
|
|
fclose($Handle);
|
|
|
|
/* Remove temporary file */
|
|
unlink($TemporaryFile);
|
|
}
|
|
|
|
/**
|
|
* Remove object older than the specified TS
|
|
* @param int $Expiry
|
|
*/
|
|
public function removeOlderThan($Expiry)
|
|
{
|
|
$this->dbRemoval(["Expiry" => $Expiry]);
|
|
}
|
|
|
|
/**
|
|
* Remove an object from the cache
|
|
* @param string $ID
|
|
*/
|
|
public function remove($ID)
|
|
{
|
|
$this->dbRemoval(["Name" => $ID]);
|
|
}
|
|
|
|
/**
|
|
* Remove with specified criterias.
|
|
*
|
|
* @param array $Settings
|
|
* @return int
|
|
*/
|
|
public function dbRemoval(array $Settings)
|
|
{
|
|
$ID = isset($Settings["Name"]) ? $Settings["Name"] : null;
|
|
$Expiry = isset($Settings["Expiry"]) ? $Settings["Expiry"] : -(24 * 60 * 60);
|
|
$TS = time() - $Expiry;
|
|
|
|
/* Compute the paths */
|
|
$Database = $this->CacheFolder . "/" . $this->CacheDB;
|
|
$Index = $this->CacheFolder . "/" . $this->CacheIndex;
|
|
$DatabaseTemp = $this->CacheFolder . "/" . $this->CacheDB . ".tmp";
|
|
$IndexTemp = $this->CacheFolder . "/" . $this->CacheIndex . ".tmp";
|
|
|
|
/* Single file removal */
|
|
if ($ID != null) {
|
|
/* Retrieve object informations */
|
|
$Object = $this->isInCache($ID, true);
|
|
|
|
/* If it's not in the cache DB, go away */
|
|
if (!$Object) {
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/* Create the temporary files */
|
|
if (!file_exists($DatabaseTemp)) {
|
|
touch($DatabaseTemp);
|
|
}
|
|
if (!file_exists($IndexTemp)) {
|
|
touch($IndexTemp);
|
|
}
|
|
|
|
/* Open the file handles */
|
|
$IndexHandle = @fopen($Index, "r");
|
|
$IndexTempHandle = @fopen($IndexTemp, "w");
|
|
$DBHandle = @fopen($Database, "r");
|
|
$DBTempHandle = @fopen($DatabaseTemp, "w");
|
|
|
|
/* Remove the selected ID from the database */
|
|
while (!feof($IndexHandle)) {
|
|
$Entry = fgets($IndexHandle, 4096);
|
|
$Entry = str_replace("\r", "", $Entry);
|
|
$Entry = str_replace("\n", "", $Entry);
|
|
$Settings = preg_split("/,/", $Entry);
|
|
|
|
if ($Entry != "") {
|
|
$PicID = $Settings[0];
|
|
$DBPos = $Settings[1];
|
|
$PicSize = $Settings[2];
|
|
$GeneratedTS = $Settings[3];
|
|
$Hits = $Settings[4];
|
|
|
|
if ($Settings[0] != $ID && $GeneratedTS > $TS) {
|
|
$CurrentPos = ftell($DBTempHandle);
|
|
fwrite(
|
|
$IndexTempHandle,
|
|
sprintf(
|
|
"%s,%s,%s,%s,%s\r\n",
|
|
$PicID,
|
|
$CurrentPos,
|
|
$PicSize,
|
|
$GeneratedTS,
|
|
$Hits
|
|
)
|
|
);
|
|
|
|
fseek($DBHandle, $DBPos);
|
|
$Picture = fread($DBHandle, $PicSize);
|
|
fwrite($DBTempHandle, $Picture);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Close the handles */
|
|
fclose($IndexHandle);
|
|
fclose($IndexTempHandle);
|
|
fclose($DBHandle);
|
|
fclose($DBTempHandle);
|
|
|
|
/* Remove the prod files */
|
|
unlink($Database);
|
|
unlink($Index);
|
|
|
|
/* Swap the temp & prod DB */
|
|
rename($DatabaseTemp, $Database);
|
|
rename($IndexTemp, $Index);
|
|
}
|
|
|
|
/**
|
|
* Is the file in cache?
|
|
*
|
|
* @param string $ID
|
|
* @param boolean $Verbose
|
|
* @param boolean $UpdateHitsCount
|
|
* @return boolean
|
|
*/
|
|
public function isInCache($ID, $Verbose = false, $UpdateHitsCount = false)
|
|
{
|
|
/* Compute the paths */
|
|
$Index = $this->CacheFolder . "/" . $this->CacheIndex;
|
|
|
|
/* Search the picture in the index file */
|
|
$Handle = @fopen($Index, "r");
|
|
while (!feof($Handle)) {
|
|
$IndexPos = ftell($Handle);
|
|
$Entry = fgets($Handle, 4096);
|
|
if ($Entry != "") {
|
|
$Settings = preg_split("/,/", $Entry);
|
|
$PicID = $Settings[0];
|
|
if ($PicID == $ID) {
|
|
fclose($Handle);
|
|
|
|
$DBPos = $Settings[1];
|
|
$PicSize = $Settings[2];
|
|
$GeneratedTS = $Settings[3];
|
|
$Hits = intval($Settings[4]);
|
|
|
|
if ($UpdateHitsCount) {
|
|
$Hits++;
|
|
if (strlen($Hits) < 7) {
|
|
$Hits = $Hits . str_repeat(" ", 7 - strlen($Hits));
|
|
}
|
|
|
|
$Handle = @fopen($Index, "r+");
|
|
fseek($Handle, $IndexPos);
|
|
fwrite(
|
|
$Handle,
|
|
sprintf(
|
|
"%s,%s,%s,%s,%s\r\n",
|
|
$PicID,
|
|
$DBPos,
|
|
$PicSize,
|
|
$GeneratedTS,
|
|
$Hits
|
|
)
|
|
);
|
|
fclose($Handle);
|
|
}
|
|
|
|
if ($Verbose) {
|
|
return [
|
|
"DBPos" => $DBPos,
|
|
"PicSize" => $PicSize,
|
|
"GeneratedTS" => $GeneratedTS,
|
|
"Hits" => $Hits
|
|
];
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fclose($Handle);
|
|
|
|
/* Picture isn't in the cache */
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Automatic output method based on the calling interface
|
|
* @param string $ID
|
|
* @param string $Destination
|
|
*/
|
|
public function autoOutput($ID, $Destination = "output.png")
|
|
{
|
|
if (php_sapi_name() == "cli") {
|
|
$this->saveFromCache($ID, $Destination);
|
|
} else {
|
|
$this->strokeFromCache($ID);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show image from cache
|
|
* @param string $ID
|
|
* @return boolean
|
|
*/
|
|
public function strokeFromCache($ID)
|
|
{
|
|
/* Get the raw picture from the cache */
|
|
$Picture = $this->getFromCache($ID);
|
|
|
|
/* Do we have a hit? */
|
|
if ($Picture == null) {
|
|
return false;
|
|
}
|
|
|
|
header('Content-type: image/png');
|
|
echo $Picture;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Save file from cache.
|
|
* @param string $ID
|
|
* @param string $Destination
|
|
* @return boolean
|
|
*/
|
|
public function saveFromCache($ID, $Destination)
|
|
{
|
|
/* Get the raw picture from the cache */
|
|
$Picture = $this->getFromCache($ID);
|
|
|
|
/* Do we have a hit? */
|
|
if ($Picture == null) {
|
|
return false;
|
|
}
|
|
|
|
/* Flush the picture to a file */
|
|
$Handle = fopen($Destination, "w");
|
|
fwrite($Handle, $Picture);
|
|
fclose($Handle);
|
|
|
|
/* All went fine */
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get file from cache
|
|
* @param string $ID
|
|
* @return string|null
|
|
*/
|
|
public function getFromCache($ID)
|
|
{
|
|
/* Compute the path */
|
|
$Database = $this->CacheFolder . "/" . $this->CacheDB;
|
|
|
|
/* Lookup for the picture in the cache */
|
|
$CacheInfo = $this->isInCache($ID, true, true);
|
|
|
|
/* Not in the cache */
|
|
if (!$CacheInfo) {
|
|
return null;
|
|
}
|
|
|
|
/* Get the database extended information */
|
|
$DBPos = $CacheInfo["DBPos"];
|
|
$PicSize = $CacheInfo["PicSize"];
|
|
|
|
/* Extract the picture from the solid cache file */
|
|
$Handle = @fopen($Database, "r");
|
|
fseek($Handle, $DBPos);
|
|
$Picture = fread($Handle, $PicSize);
|
|
fclose($Handle);
|
|
|
|
/* Return back the raw picture data */
|
|
return $Picture;
|
|
}
|
|
}
|