OXID eShop CE  4.10.1
 All Classes Namespaces Files Functions Variables Pages
oxdynimggenerator.php
Go to the documentation of this file.
1 <?php
2 
3 require_once dirname(__FILE__) . "/../bootstrap.php";
4 
5 // Checks if base path getter does not exist
6 if (!function_exists("getShopBasePath")) {
12  function getShopBasePath()
13  {
14  return realpath(dirname(__FILE__) . '/..') . '/';
15  }
16 }
17 
18 
19 // disables admin
20 if (!function_exists('isAdmin')) {
26  function isAdmin()
27  {
28  return false;
29  }
30 }
31 
32 // starts shop framework and returns config instance
33 if (!function_exists("getConfig")) {
39  function getConfig()
40  {
41  // custom functions file
42  include_once getShopBasePath() . 'modules/functions.php';
43 
44  // Generic utility method file
45  include_once getShopBasePath() . 'core/oxfunctions.php';
46 
47  // initializes singleton config class
48  return oxRegistry::getConfig();
49  }
50 }
51 
52 // Checks if instance name getter does not exist
53 if (!function_exists("getGeneratorInstanceName")) {
59  function getGeneratorInstanceName()
60  {
61  return "oxdynimggenerator";
62  }
63 }
64 
65 // checks if GD library version getter does not exist
66 if (!function_exists("getGdVersion")) {
72  function getGdVersion()
73  {
74  static $iVersion = null;
75 
76  if ($iVersion === null) {
77  $iVersion = false;
78  if (function_exists("gd_info")) {
79  // extracting GD version from php
80  $aInfo = gd_info();
81  if (isset($aInfo["GD Version"])) {
82  $iVersion = version_compare(preg_replace("/[^0-9\.]/", "", $aInfo["GD Version"]), 1, '>') ? 2 : 1;
83  }
84  }
85 
86  }
87 
88  return $iVersion;
89  }
90 }
91 
92 // checks if image utils file loader does not exist
93 if (!function_exists("includeImageUtils")) {
97  function includeImageUtils()
98  {
99  include_once getShopBasePath() . "core/utils/oxpicgenerator.php";
100  }
101 }
102 
107 {
108 
114  protected static $_oInstance = null;
115 
121  protected $_aHeaders = array();
122 
128  protected $_aAllowedImgTypes = array("jpg", "jpeg", "png", "gif");
129 
136  protected $_sImageInfoSep = "_";
137 
143  protected $_hLockHandle = null;
144 
150  protected $_sImageUri = null;
151 
157  protected $_aConfParamToPath = array( // ** product
158  "sIconsize" => '/.*\/generated\/product\/(icon|\d+)\/\d+\_\d+\_\d+$/', // Icon size
159  "sThumbnailsize" => '/.*\/generated\/product\/(thumb|\d+)\/\d+\_\d+\_\d+$/', // Thumbnail size
160  "sZoomImageSize" => '/.*\/generated\/product\/\d+\/\d+\_\d+\_\d+$/', // Zoom picture size
161  "aDetailImageSizes" => '/.*\/generated\/product\/\d+\/\d+\_\d+\_\d+$/', // Product picture size
162 
163  // ** manufacturer/vendor
164  "sManufacturerIconsize" => '/.*\/generated\/(manufacturer|vendor)\/icon\/\d+\_\d+\_\d+$/', // Manufacturer's|brand logo size
165 
166  // ** category
167  "sCatThumbnailsize" => '/.*\/generated\/category\/thumb\/\d+\_\d+\_\d+$/', // Category picture size
168  "sCatIconsize" => '/.*\/generated\/category\/icon\/\d+\_\d+\_\d+$/', // Size of a subcategory's picture
169  "sCatPromotionsize" => '/.*\/generated\/category\/promo_icon\/\d+\_\d+\_\d+$/' // Category picture size for promotion on startpage
170  );
171 
177  public static function getInstance()
178  {
179  if (self::$_oInstance === null) {
180  $sInstanceName = getGeneratorInstanceName();
181  self::$_oInstance = new $sInstanceName();
182  }
183 
184  return self::$_oInstance;
185  }
186 
198  public function __call($sMethod, $aArgs)
199  {
200  if (defined('OXID_PHP_UNIT')) {
201  if (substr($sMethod, 0, 4) == "UNIT") {
202  $sMethod = str_replace("UNIT", "_", $sMethod);
203  }
204  if (method_exists($this, $sMethod)) {
205  return call_user_func_array(array(& $this, $sMethod), $aArgs);
206  }
207  }
208 
209  throw new oxSystemComponentException("Function '$sMethod' does not exist or is not accessible! (" . get_class($this) . ")" . PHP_EOL);
210  }
211 
217  protected function _getShopBasePath()
218  {
219  return getConfig()->getConfigParam("sShopDir");
220  }
221 
227  protected function _getImageUri()
228  {
229  if ($this->_sImageUri === null) {
230 
231  $this->_sImageUri = "";
232  $sReqPath = "out/pictures/generated";
233 
234 
235  $sReqImg = isset($_SERVER["REQUEST_URI"]) ? urldecode($_SERVER["REQUEST_URI"]) : "";
236  if (($iPos = strpos($sReqImg, $sReqPath)) !== false) {
237  $this->_sImageUri = substr($sReqImg, $iPos);
238  }
239 
240  $this->_sImageUri = trim($this->_sImageUri, "/");
241  }
242 
243  return $this->_sImageUri;
244  }
245 
251  protected function _getImageName()
252  {
253  return basename($this->_getImageUri());
254  }
255 
261  protected function _getImageMasterPath()
262  {
263  $sUri = $this->_getImageUri();
264  $sPath = false;
265 
266  if ($sUri && ($sPath = dirname(dirname($sUri)))) {
267  $sPath = preg_replace("/\/([^\/]*)\/([^\/]*)\/([^\/]*)$/", "/master/\\2/\\3/", $sPath);
268  }
269 
270  return $sPath;
271  }
272 
278  protected function _getImageInfo()
279  {
280  $aInfo = array();
281  if (($sUri = $this->_getImageUri())) {
282  $aInfo = explode($this->_sImageInfoSep, basename(dirname($sUri)));
283  }
284 
285  return $aInfo;
286  }
287 
293  protected function _getImageTarget()
294  {
295  return $this->_getShopBasePath() . $this->_getImageUri();
296  }
297 
303  protected function _getNopicImageTarget()
304  {
305  $sPath = $this->_getShopBasePath() . $this->_getImageUri();
306 
307  return str_replace($this->_getImageName(), "nopic.jpg", $sPath);
308  }
309 
315  protected function _getImageType()
316  {
317  $sType = preg_replace("/.*\.(png|jp(e)?g|gif)$/", "\\1", $this->_getImageName());
318  $sType = (strcmp($sType, "jpg") == 0) ? "jpeg" : $sType;
319 
320  return in_array($sType, $this->_aAllowedImgTypes) ? $sType : false;
321  }
322 
333  protected function _generatePng($sSource, $sTarget, $iWidth, $iHeight)
334  {
335  return resizePng($sSource, $sTarget, $iWidth, $iHeight, @getimagesize($sSource), getGdVersion(), null);
336  }
337 
349  protected function _generateJpg($sSource, $sTarget, $iWidth, $iHeight, $iQuality)
350  {
351  return resizeJpeg($sSource, $sTarget, $iWidth, $iHeight, @getimagesize($sSource), getGdVersion(), null, $iQuality);
352  }
353 
364  protected function _generateGif($sSource, $sTarget, $iWidth, $iHeight)
365  {
366  $aImageInfo = @getimagesize($sSource);
367 
368  return resizeGif($sSource, $sTarget, $iWidth, $iHeight, $aImageInfo[0], $aImageInfo[1], getGdVersion());
369  }
370 
379  protected function _isTargetPathValid($sPath)
380  {
381  $blValid = true;
382  $sDir = dirname(trim($sPath));
383 
384  // first time folder access?
385  if (!is_dir($sDir) && ($blValid = $this->_isValidPath($sDir))) {
386  // creating missing folders
387  $blValid = $this->_createFolders($sDir);
388  }
389 
390  return $blValid;
391  }
392 
400  protected function _createFolders($sDir)
401  {
402  $oConfig = getConfig();
403  $sPicFolderPath = dirname($oConfig->getMasterPictureDir());
404 
405  $blDone = false;
406  if ($sPicFolderPath && is_dir($sPicFolderPath)) {
407 
408  // if its in main path..
409  if (strcmp($sPicFolderPath, substr($sDir, 0, strlen($sPicFolderPath))) == 0) {
410  // folder does not exist yet?
411  if (!($blDone = file_exists($sDir))) {
412  clearstatcache();
413  // in case creation did not succeed, maybe another process allready created folder?
414  $iMode = 0755;
415  if (defined('OXID_PHP_UNIT')) {
416  $iMode = 0777;
417  }
418  $blDone = mkdir($sDir, $iMode, true) || file_exists($sDir);
419  }
420  }
421  }
422 
423  return $blDone;
424  }
425 
433  protected function _isValidPath($sPath)
434  {
435  $blValid = false;
436 
437  list($iWidth, $iHeight, $sQuality) = $this->_getImageInfo();
438  if ($iWidth && $iHeight && $sQuality) {
439 
440  $oConfig = getConfig();
442 
443  // parameter names
444  $sNames = '';
445  foreach ($this->_aConfParamToPath as $sParamName => $sPathReg) {
446  if (preg_match($sPathReg, $sPath)) {
447  if ($sNames) {
448  $sNames .= ", ";
449  }
450  $sNames .= $oDb->quote($sParamName);
451 
452  if ($sParamName == "sManufacturerIconsize" || $sParamName == "sCatIconsize") {
453  $sNames .= ", " . $oDb->quote("sIconsize");
454  }
455  }
456  }
457 
458  // any name matching path?
459  if ($sNames) {
460 
461  $sDecodeField = $oConfig->getDecodeValueQuery();
462 
463  // selecting shop which image quality matches user given
464  $sQ = "select oxshopid from oxconfig where oxvarname = 'sDefaultImageQuality' and
465  {$sDecodeField} = " . $oDb->quote($sQuality);
466 
467  $aShopIds = $oDb->getAll($sQ);
468 
469  // building query:
470  // shop id
471  $sShopIds = '';
472  foreach ($aShopIds as $aShopId) {
473 
474  // probably here we can resolve and check shop id to shorten check?
475 
476 
477  if ($sShopIds) {
478  $sShopIds .= ", ";
479  }
480  $sShopIds .= $oDb->quote($aShopId["oxshopid"]);
481  }
482 
483  // any shop matching quality
484  if ($sShopIds) {
485 
486  //
487  $sCheckSize = "$iWidth*$iHeight";
488 
489  // selecting config variables to check
490  $sQ = "select oxvartype, {$sDecodeField} as oxvarvalue from oxconfig
491  where oxvarname in ( {$sNames} ) and oxshopid in ( {$sShopIds} ) order by oxshopid";
492 
493  $aValues = $oDb->getAll($sQ);
494  foreach ($aValues as $aValue) {
495  $aConfValues = (array) $oConfig->decodeValue($aValue["oxvartype"], $aValue["oxvarvalue"]);
496  foreach ($aConfValues as $sValue) {
497  if (strcmp($sCheckSize, $sValue) == 0) {
498  $blValid = true;
499  break;
500  }
501  }
502  }
503  }
504  }
505  }
506 
507  return $blValid;
508  }
509 
518  protected function _generateImage($sImageSource, $sImageTarget)
519  {
520  $sPath = false;
521 
522  if (getGdVersion() !== false && $this->_isTargetPathValid($sImageTarget) && ($sImageType = $this->_getImageType())) {
523 
524  // including generator files
525  includeImageUtils();
526 
527  // in case lock file creation failed should check if another process did not created image yet
528  if ($this->_lock($sImageTarget)) {
529 
530  // extracting image info - size/quality
531  list($iWidth, $iHeight, $iQuality) = $this->_getImageInfo();
532  switch ($sImageType) {
533  case "png":
534  $sPath = $this->_generatePng($sImageSource, $sImageTarget, $iWidth, $iHeight);
535  break;
536  case "jpeg":
537  $sPath = $this->_generateJpg($sImageSource, $sImageTarget, $iWidth, $iHeight, $iQuality);
538  break;
539  case "gif":
540  $sPath = $this->_generateGif($sImageSource, $sImageTarget, $iWidth, $iHeight);
541  break;
542  }
543 
544  // releasing..
545  if ($sPath) {
546  $this->_unlock($sImageTarget);
547  }
548  } else {
549  // assuming that image was created by another process
550  $sPath = file_exists($sImageTarget) ? $sImageTarget : false;
551  }
552  }
553 
554  return $sPath;
555  }
556 
564  protected function _getLockName($sName)
565  {
566  return "$sName.lck";
567  }
568 
576  protected function _lock($sSource)
577  {
578  $blLocked = false;
579  $sLockName = $this->_getLockName($sSource);
580 
581  // creating lock file
582  $this->_hLockHandle = @fopen($this->_getLockName($sSource), "w");
583  if ($this->_hLockHandle) {
584  if (!($blLocked = flock($this->_hLockHandle, LOCK_EX))) {
585  // on failure - closing
586  fclose($rHandle);
587  }
588  }
589 
590  // in case system does not support file lockings
591  if (!$blLocked) {
592  // start a blank file to inform other processes we are dealing with it.
593  if (!(file_exists($this->_getLockName($sSource)) && abs(time() - filectime($this->_getLockName($sSource)) < 40))) {
594  if ($this->_hLockHandle = @fopen($this->_getLockName($sSource), "w")) {
595  $blLocked = true;
596  }
597  }
598  }
599 
600  return $blLocked;
601  }
602 
608  protected function _unlock($sSource)
609  {
610  if ($this->_hLockHandle) {
611  flock($this->_hLockHandle, LOCK_UN);
612  fclose($this->_hLockHandle);
613  unlink($this->_getLockName($sSource));
614  }
615  }
616 
625  public function getImagePath($sAbsPath = false)
626  {
627  if ($sAbsPath) {
628  $this->_sImageUri = str_replace($this->_getShopBasePath(), "", $sAbsPath);
629  }
630 
631  $sImagePath = false;
632  $sMasterPath = $this->_getImageMasterPath();
633 
634  // building base path + extracting image name + extracting master image path
635  $sMasterImagePath = $this->_getShopBasePath() . $sMasterPath . $this->_getImageName();
636 
637  if (file_exists($sMasterImagePath)) {
638  $sGenImagePath = $this->_getImageTarget();
639  } else {
640  // nopic master path
641  $sMasterImagePath = $this->_getShopBasePath() . dirname(dirname($sMasterPath)) . "/nopic.jpg";
642  $sGenImagePath = $this->_getNopicImageTarget();
643 
644  // 404 header for nopic
645  $this->_setHeader("HTTP/1.0 404 Not Found");
646  }
647 
648  // checking if master image is accessible
649  if (file_exists($sGenImagePath)) {
650  $sImagePath = $sGenImagePath;
651  } elseif (file_exists($sMasterImagePath)) {
652  // generating image
653  $sImagePath = $this->_generateImage($sMasterImagePath, $sGenImagePath);
654  }
655 
656  if ($sImagePath) {
657  // image type header
658  $this->_setHeader("Content-Type: image/" . $this->_getImageType());
659  } else {
660  // unable to output any file
661  $this->_setHeader("HTTP/1.0 404 Not Found");
662  }
663 
664  return $sImagePath;
665  }
666 
672  public function outputImage()
673  {
674  $blBuffer = true;
675  if (defined('OXID_PHP_UNIT')) {
676  $blBuffer = false;
677  }
678 
679  // starting output buffering
680  if ($blBuffer) {
681  ob_start();
682  }
683 
684  //
685  $sImgPath = $this->getImagePath();
686 
687  // cleaning extra output
688  if ($blBuffer) {
689  ob_clean();
690  }
691 
692  // outputting headers
693  $aHeaders = $this->_getHeaders();
694  foreach ($aHeaders as $sHeader) {
695  header($sHeader);
696  }
697 
698  // sending headers
699  if ($blBuffer) {
700  ob_end_flush();
701  }
702 
703  // file is generated?
704  if ($sImgPath) {
705  // outputting file
706  @readfile($sImgPath);
707  }
708  }
709 
715  protected function _setHeader($sHeader)
716  {
717  $this->_aHeaders[] = $sHeader;
718  }
719 
725  protected function _getHeaders()
726  {
727  return $this->_aHeaders;
728  }
729 }