OXID eShop CE  4.10.7
 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  $sReqImg = str_replace('//', '/', $sReqImg);
237  if (($iPos = strpos($sReqImg, $sReqPath)) !== false) {
238  $this->_sImageUri = substr($sReqImg, $iPos);
239  }
240 
241  $this->_sImageUri = trim($this->_sImageUri, "/");
242  }
243 
244  return $this->_sImageUri;
245  }
246 
252  protected function _getImageName()
253  {
254  return basename($this->_getImageUri());
255  }
256 
262  protected function _getImageMasterPath()
263  {
264  $sUri = $this->_getImageUri();
265  $sPath = false;
266 
267  if ($sUri && ($sPath = dirname(dirname($sUri)))) {
268  $sPath = preg_replace("/\/([^\/]*)\/([^\/]*)\/([^\/]*)$/", "/master/\\2/\\3/", $sPath);
269  }
270 
271  return $sPath;
272  }
273 
279  protected function _getImageInfo()
280  {
281  $aInfo = array();
282  if (($sUri = $this->_getImageUri())) {
283  $aInfo = explode($this->_sImageInfoSep, basename(dirname($sUri)));
284  }
285 
286  return $aInfo;
287  }
288 
294  protected function _getImageTarget()
295  {
296  return $this->_getShopBasePath() . $this->_getImageUri();
297  }
298 
304  protected function _getNopicImageTarget()
305  {
306  $sPath = $this->_getShopBasePath() . $this->_getImageUri();
307 
308  return str_replace($this->_getImageName(), "nopic.jpg", $sPath);
309  }
310 
316  protected function _getImageType()
317  {
318  $sType = preg_replace("/.*\.(png|jp(e)?g|gif)$/", "\\1", $this->_getImageName());
319  $sType = (strcmp($sType, "jpg") == 0) ? "jpeg" : $sType;
320 
321  return in_array($sType, $this->_aAllowedImgTypes) ? $sType : false;
322  }
323 
334  protected function _generatePng($sSource, $sTarget, $iWidth, $iHeight)
335  {
336  return resizePng($sSource, $sTarget, $iWidth, $iHeight, @getimagesize($sSource), getGdVersion(), null);
337  }
338 
350  protected function _generateJpg($sSource, $sTarget, $iWidth, $iHeight, $iQuality)
351  {
352  return resizeJpeg($sSource, $sTarget, $iWidth, $iHeight, @getimagesize($sSource), getGdVersion(), null, $iQuality);
353  }
354 
365  protected function _generateGif($sSource, $sTarget, $iWidth, $iHeight)
366  {
367  $aImageInfo = @getimagesize($sSource);
368 
369  return resizeGif($sSource, $sTarget, $iWidth, $iHeight, $aImageInfo[0], $aImageInfo[1], getGdVersion());
370  }
371 
380  protected function _isTargetPathValid($sPath)
381  {
382  $blValid = true;
383  $sDir = dirname(trim($sPath));
384 
385  // first time folder access?
386  if (!is_dir($sDir) && ($blValid = $this->_isValidPath($sDir))) {
387  // creating missing folders
388  $blValid = $this->_createFolders($sDir);
389  }
390 
391  return $blValid;
392  }
393 
401  protected function _createFolders($sDir)
402  {
403  $oConfig = getConfig();
404  $sPicFolderPath = dirname($oConfig->getMasterPictureDir());
405 
406  $blDone = false;
407  if ($sPicFolderPath && is_dir($sPicFolderPath)) {
408 
409  // if its in main path..
410  if (strcmp($sPicFolderPath, substr($sDir, 0, strlen($sPicFolderPath))) == 0) {
411  // folder does not exist yet?
412  if (!($blDone = file_exists($sDir))) {
413  clearstatcache();
414  // in case creation did not succeed, maybe another process allready created folder?
415  $iMode = 0755;
416  if (defined('OXID_PHP_UNIT')) {
417  $iMode = 0777;
418  }
419  $blDone = mkdir($sDir, $iMode, true) || file_exists($sDir);
420  }
421  }
422  }
423 
424  return $blDone;
425  }
426 
434  protected function _isValidPath($sPath)
435  {
436  $blValid = false;
437 
438  list($iWidth, $iHeight, $sQuality) = $this->_getImageInfo();
439  if ($iWidth && $iHeight && $sQuality) {
440 
441  $oConfig = getConfig();
443 
444  // parameter names
445  $sNames = '';
446  foreach ($this->_aConfParamToPath as $sParamName => $sPathReg) {
447  if (preg_match($sPathReg, $sPath)) {
448  if ($sNames) {
449  $sNames .= ", ";
450  }
451  $sNames .= $oDb->quote($sParamName);
452 
453  if ($sParamName == "sManufacturerIconsize" || $sParamName == "sCatIconsize") {
454  $sNames .= ", " . $oDb->quote("sIconsize");
455  }
456  }
457  }
458 
459  // any name matching path?
460  if ($sNames) {
461 
462  $sDecodeField = $oConfig->getDecodeValueQuery();
463 
464  // selecting shop which image quality matches user given
465  $sQ = "select oxshopid from oxconfig where oxvarname = 'sDefaultImageQuality' and
466  {$sDecodeField} = " . $oDb->quote($sQuality);
467 
468  $aShopIds = $oDb->getAll($sQ);
469 
470  // building query:
471  // shop id
472  $sShopIds = '';
473  foreach ($aShopIds as $aShopId) {
474 
475  // probably here we can resolve and check shop id to shorten check?
476 
477 
478  if ($sShopIds) {
479  $sShopIds .= ", ";
480  }
481  $sShopIds .= $oDb->quote($aShopId["oxshopid"]);
482  }
483 
484  // any shop matching quality
485  if ($sShopIds) {
486 
487  //
488  $sCheckSize = "$iWidth*$iHeight";
489 
490  // selecting config variables to check
491  $sQ = "select oxvartype, {$sDecodeField} as oxvarvalue from oxconfig
492  where oxvarname in ( {$sNames} ) and oxshopid in ( {$sShopIds} ) order by oxshopid";
493 
494  $aValues = $oDb->getAll($sQ);
495  foreach ($aValues as $aValue) {
496  $aConfValues = (array) $oConfig->decodeValue($aValue["oxvartype"], $aValue["oxvarvalue"]);
497  foreach ($aConfValues as $sValue) {
498  if (strcmp($sCheckSize, $sValue) == 0) {
499  $blValid = true;
500  break;
501  }
502  }
503  }
504  }
505  }
506  }
507 
508  return $blValid;
509  }
510 
519  protected function _generateImage($sImageSource, $sImageTarget)
520  {
521  $sPath = false;
522  $fileExtensionSource = strtolower(pathinfo($sImageSource, PATHINFO_EXTENSION));
523 
524  if (getGdVersion() !== false && $this->_isTargetPathValid($sImageTarget) && ($sImageType = $this->_getImageType())) {
525 
526  // including generator files
527  includeImageUtils();
528 
529  // in case lock file creation failed should check if another process did not created image yet
530  if ($this->_lock($sImageTarget)) {
531 
532  // extracting image info - size/quality
533  list($iWidth, $iHeight, $iQuality) = $this->_getImageInfo();
534  switch ($fileExtensionSource) {
535  case "png":
536  $sPath = $this->_generatePng($sImageSource, $sImageTarget, $iWidth, $iHeight);
537  break;
538  case "jpeg":
539  case "jpg":
540  $sPath = $this->_generateJpg($sImageSource, $sImageTarget, $iWidth, $iHeight, $iQuality);
541  break;
542  case "gif":
543  $sPath = $this->_generateGif($sImageSource, $sImageTarget, $iWidth, $iHeight);
544  break;
545  }
546 
547  // releasing..
548  if ($sPath) {
549  $this->_unlock($sImageTarget);
550  }
551  } else {
552  // assuming that image was created by another process
553  $sPath = file_exists($sImageTarget) ? $sImageTarget : false;
554  }
555  }
556 
557  return $sPath;
558  }
559 
567  protected function _getLockName($sName)
568  {
569  return "$sName.lck";
570  }
571 
579  protected function _lock($sSource)
580  {
581  $blLocked = false;
582  $sLockName = $this->_getLockName($sSource);
583 
584  // creating lock file
585  $this->_hLockHandle = @fopen($this->_getLockName($sSource), "w");
586  if ($this->_hLockHandle) {
587  if (!($blLocked = flock($this->_hLockHandle, LOCK_EX))) {
588  // on failure - closing
589  fclose($this->_hLockHandle);
590  }
591  }
592 
593  // in case system does not support file lockings
594  if (!$blLocked) {
595  // start a blank file to inform other processes we are dealing with it.
596  if (!(file_exists($this->_getLockName($sSource)) && abs(time() - filectime($this->_getLockName($sSource)) < 40))) {
597  if ($this->_hLockHandle = @fopen($this->_getLockName($sSource), "w")) {
598  $blLocked = true;
599  }
600  }
601  }
602 
603  return $blLocked;
604  }
605 
611  protected function _unlock($sSource)
612  {
613  if ($this->_hLockHandle) {
614  flock($this->_hLockHandle, LOCK_UN);
615  fclose($this->_hLockHandle);
616  unlink($this->_getLockName($sSource));
617  }
618  }
619 
628  public function getImagePath($sAbsPath = false)
629  {
630  if ($sAbsPath) {
631  $this->_sImageUri = str_replace($this->_getShopBasePath(), "", $sAbsPath);
632  }
633 
634  $sImagePath = false;
635  $sMasterPath = $this->_getImageMasterPath();
636 
637  // building base path + extracting image name + extracting master image path
638  $sMasterImagePath = $this->_getShopBasePath() . $sMasterPath . $this->_getImageName();
639 
640  if (file_exists($sMasterImagePath)) {
641  $sGenImagePath = $this->_getImageTarget();
642  } else {
643  // nopic master path
644  $sMasterImagePath = $this->_getShopBasePath() . dirname(dirname($sMasterPath)) . "/nopic.jpg";
645  $sGenImagePath = $this->_getNopicImageTarget();
646 
647  // 404 header for nopic
648  $this->_setHeader("HTTP/1.0 404 Not Found");
649  }
650 
651  // checking if master image is accessible
652  if (file_exists($sGenImagePath)) {
653  $sImagePath = $sGenImagePath;
654  } elseif (file_exists($sMasterImagePath)) {
655  // generating image
656  $sImagePath = $this->_generateImage($sMasterImagePath, $sGenImagePath);
657  }
658 
659  if ($sImagePath) {
660  // image type header
661  $this->_setHeader("Content-Type: image/" . $this->_getImageType());
662  } else {
663  // unable to output any file
664  $this->_setHeader("HTTP/1.0 404 Not Found");
665  }
666 
667  return $sImagePath;
668  }
669 
675  public function outputImage()
676  {
677  $blBuffer = true;
678  if (defined('OXID_PHP_UNIT')) {
679  $blBuffer = false;
680  }
681 
682  // starting output buffering
683  if ($blBuffer) {
684  ob_start();
685  }
686 
687  //
688  $sImgPath = $this->getImagePath();
689 
690  // cleaning extra output
691  if ($blBuffer) {
692  ob_clean();
693  }
694 
695  // outputting headers
696  $aHeaders = $this->_getHeaders();
697  foreach ($aHeaders as $sHeader) {
698  header($sHeader);
699  }
700 
701  // sending headers
702  if ($blBuffer) {
703  ob_end_flush();
704  }
705 
706  // file is generated?
707  if ($sImgPath) {
708  // outputting file
709  @readfile($sImgPath);
710  }
711  }
712 
718  protected function _setHeader($sHeader)
719  {
720  $this->_aHeaders[] = $sHeader;
721  }
722 
728  protected function _getHeaders()
729  {
730  return $this->_aHeaders;
731  }
732 }