1<?php
2namespace Amenadiel\JpGraph\Image;
3
4use Amenadiel\JpGraph\Util;
5
6//=======================================================================
7// CLASS ImgStreamCache
8// Description: Handle caching of graphs to files. All image output goes
9//              through this class
10//=======================================================================
11class ImgStreamCache
12{
13    private $cache_dir;
14    private $timeout = 0; // Infinite timeout
15    //---------------
16    // CONSTRUCTOR
17    public function __construct($aCacheDir = CACHE_DIR)
18    {
19        $this->cache_dir = $aCacheDir;
20    }
21
22    //---------------
23    // PUBLIC METHODS
24
25    // Specify a timeout (in minutes) for the file. If the file is older then the
26    // timeout value it will be overwritten with a newer version.
27    // If timeout is set to 0 this is the same as infinite large timeout and if
28    // timeout is set to -1 this is the same as infinite small timeout
29    public function SetTimeout($aTimeout)
30    {
31        $this->timeout = $aTimeout;
32    }
33
34    // Output image to browser and also write it to the cache
35    public function PutAndStream($aImage, $aCacheFileName, $aInline, $aStrokeFileName)
36    {
37
38        // Check if we should always stroke the image to a file
39        if (_FORCE_IMGTOFILE) {
40            $aStrokeFileName = _FORCE_IMGDIR . GenImgName();
41        }
42
43        if ($aStrokeFileName != '') {
44            if ($aStrokeFileName == 'auto') {
45                $aStrokeFileName = GenImgName();
46            }
47
48            if (file_exists($aStrokeFileName)) {
49
50                // Wait for lock (to make sure no readers are trying to access the image)
51                $fd   = fopen($aStrokeFileName, 'w');
52                $lock = flock($fd, LOCK_EX);
53
54                // Since the image write routines only accepts a filename which must not
55                // exist we need to delete the old file first
56                if (!@unlink($aStrokeFileName)) {
57                    $lock = flock($fd, LOCK_UN);
58                    Util\JpGraphError::RaiseL(25111, $aStrokeFileName);
59                    //(" Can't delete cached image $aStrokeFileName. Permission problem?");
60                }
61                $aImage->Stream($aStrokeFileName);
62                $lock = flock($fd, LOCK_UN);
63                fclose($fd);
64            } else {
65                $aImage->Stream($aStrokeFileName);
66            }
67
68            return;
69        }
70
71        if ($aCacheFileName != '' && USE_CACHE) {
72            $aCacheFileName = $this->cache_dir . $aCacheFileName;
73            if (file_exists($aCacheFileName)) {
74                if (!$aInline) {
75                    // If we are generating image off-line (just writing to the cache)
76                    // and the file exists and is still valid (no timeout)
77                    // then do nothing, just return.
78                    $diff = time() - filemtime($aCacheFileName);
79                    if ($diff < 0) {
80                        Util\JpGraphError::RaiseL(25112, $aCacheFileName);
81                        //(" Cached imagefile ($aCacheFileName) has file date in the future!!");
82                    }
83                    if ($this->timeout > 0 && ($diff <= $this->timeout * 60)) {
84                        return;
85                    }
86                }
87
88                // Wait for lock (to make sure no readers are trying to access the image)
89                $fd   = fopen($aCacheFileName, 'w');
90                $lock = flock($fd, LOCK_EX);
91
92                if (!@unlink($aCacheFileName)) {
93                    $lock = flock($fd, LOCK_UN);
94                    Util\JpGraphError::RaiseL(25113, $aStrokeFileName);
95                    //(" Can't delete cached image $aStrokeFileName. Permission problem?");
96                }
97                $aImage->Stream($aCacheFileName);
98                $lock = flock($fd, LOCK_UN);
99                fclose($fd);
100            } else {
101                $this->MakeDirs(dirname($aCacheFileName));
102                if (!is_writeable(dirname($aCacheFileName))) {
103                    Util\JpGraphError::RaiseL(25114, $aCacheFileName);
104                    //('PHP has not enough permissions to write to the cache file '.$aCacheFileName.'. Please make sure that the user running PHP has write permission for this file if you wan to use the cache system with JpGraph.');
105                }
106                $aImage->Stream($aCacheFileName);
107            }
108
109            $res = true;
110            // Set group to specified
111            if (CACHE_FILE_GROUP != '') {
112                $res = @chgrp($aCacheFileName, CACHE_FILE_GROUP);
113            }
114            if (CACHE_FILE_MOD != '') {
115                $res = @chmod($aCacheFileName, CACHE_FILE_MOD);
116            }
117            if (!$res) {
118                Util\JpGraphError::RaiseL(25115, $aStrokeFileName);
119                //(" Can't set permission for cached image $aStrokeFileName. Permission problem?");
120            }
121
122            $aImage->Destroy();
123            if ($aInline) {
124                if ($fh = @fopen($aCacheFileName, "rb")) {
125                    $aImage->Headers();
126                    fpassthru($fh);
127                    return;
128                } else {
129                    Util\JpGraphError::RaiseL(25116, $aFile); //(" Cant open file from cache [$aFile]");
130                }
131            }
132        } elseif ($aInline) {
133            $aImage->Headers();
134            $aImage->Stream();
135            return;
136        }
137    }
138
139    public function IsValid($aCacheFileName)
140    {
141        $aCacheFileName = $this->cache_dir . $aCacheFileName;
142        if (USE_CACHE && file_exists($aCacheFileName)) {
143            $diff = time() - filemtime($aCacheFileName);
144            if ($this->timeout > 0 && ($diff > $this->timeout * 60)) {
145                return false;
146            } else {
147                return true;
148            }
149        } else {
150            return false;
151        }
152    }
153
154    public function StreamImgFile($aImage, $aCacheFileName)
155    {
156        $aCacheFileName = $this->cache_dir . $aCacheFileName;
157        if ($fh = @fopen($aCacheFileName, 'rb')) {
158            $lock = flock($fh, LOCK_SH);
159            $aImage->Headers();
160            fpassthru($fh);
161            $lock = flock($fh, LOCK_UN);
162            fclose($fh);
163            return true;
164        } else {
165            Util\JpGraphError::RaiseL(25117, $aCacheFileName); //(" Can't open cached image \"$aCacheFileName\" for reading.");
166        }
167    }
168
169    // Check if a given image is in cache and in that case
170    // pass it directly on to web browser. Return false if the
171    // image file doesn't exist or exists but is to old
172    public function GetAndStream($aImage, $aCacheFileName)
173    {
174        if ($this->Isvalid($aCacheFileName)) {
175            $this->StreamImgFile($aImage, $aCacheFileName);
176        } else {
177            return false;
178        }
179    }
180
181    //---------------
182    // PRIVATE METHODS
183    // Create all necessary directories in a path
184    public function MakeDirs($aFile)
185    {
186        $dirs = array();
187        // In order to better work when open_basedir is enabled
188        // we do not create directories in the root path
189        while ($aFile != '/' && !(file_exists($aFile))) {
190            $dirs[] = $aFile . '/';
191            $aFile  = dirname($aFile);
192        }
193        for ($i = sizeof($dirs) - 1; $i >= 0; $i--) {
194            if (!@mkdir($dirs[$i], 0777)) {
195                Util\JpGraphError::RaiseL(25118, $aFile); //(" Can't create directory $aFile. Make sure PHP has write permission to this directory.");
196            }
197            // We also specify mode here after we have changed group.
198            // This is necessary if Apache user doesn't belong the
199            // default group and hence can't specify group permission
200            // in the previous mkdir() call
201            if (CACHE_FILE_GROUP != "") {
202                $res = true;
203                $res = @chgrp($dirs[$i], CACHE_FILE_GROUP);
204                $res = @chmod($dirs[$i], 0777);
205                if (!$res) {
206                    Util\JpGraphError::RaiseL(25119, $aFile); //(" Can't set permissions for $aFile. Permission problems?");
207                }
208            }
209        }
210        return true;
211    }
212} // CLASS Cache
213