OXID eShop CE  4.8.12
 All Classes 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  return $iVersion;
88  }
89 }
90 
91 // checks if image utils file loader does not exist
92 if ( !function_exists( "includeImageUtils" ) ) {
98  function includeImageUtils()
99  {
100  include_once getShopBasePath() . "core/utils/oxpicgenerator.php";
101  }
102 }
103 
108 {
113  protected static $_oInstance = null;
114 
119  protected $_aHeaders = array();
120 
125  protected $_aAllowedImgTypes = array( "jpg", "jpeg", "png", "gif" );
126 
132  protected $_sImageInfoSep = "_";
133 
138  protected $_hLockHandle = null;
139 
144  protected $_sImageUri = null;
145 
150  protected $_aConfParamToPath = array( // ** product
151  "sIconsize" => '/.*\/generated\/product\/(icon|\d+)\/\d+\_\d+\_\d+$/', // Icon size
152  "sThumbnailsize" => '/.*\/generated\/product\/(thumb|\d+)\/\d+\_\d+\_\d+$/', // Thumbnail size
153  "sZoomImageSize" => '/.*\/generated\/product\/\d+\/\d+\_\d+\_\d+$/', // Zoom picture size
154  "aDetailImageSizes" => '/.*\/generated\/product\/\d+\/\d+\_\d+\_\d+$/', // Product picture size
155 
156  // ** manufacturer/vendor
157  "sManufacturerIconsize" => '/.*\/generated\/(manufacturer|vendor)\/icon\/\d+\_\d+\_\d+$/', // Manufacturer's|brand logo size
158 
159  // ** category
160  "sCatThumbnailsize" => '/.*\/generated\/category\/thumb\/\d+\_\d+\_\d+$/', // Category picture size
161  "sCatIconsize" => '/.*\/generated\/category\/icon\/\d+\_\d+\_\d+$/', // Size of a subcategory's picture
162  "sCatPromotionsize" => '/.*\/generated\/category\/promo_icon\/\d+\_\d+\_\d+$/' // Category picture size for promotion on startpage
163  );
164 
170  public static function getInstance()
171  {
172  if ( self::$_oInstance === null ) {
173  $sInstanceName = getGeneratorInstanceName();
174  self::$_oInstance = new $sInstanceName();
175  }
176  return self::$_oInstance;
177  }
178 
190  public function __call( $sMethod, $aArgs )
191  {
192  if ( defined( 'OXID_PHP_UNIT' ) ) {
193  if ( substr( $sMethod, 0, 4) == "UNIT" ) {
194  $sMethod = str_replace( "UNIT", "_", $sMethod );
195  }
196  if ( method_exists( $this, $sMethod)) {
197  return call_user_func_array( array( & $this, $sMethod ), $aArgs );
198  }
199  }
200 
201  throw new oxSystemComponentException( "Function '$sMethod' does not exist or is not accessible! (" . get_class($this) . ")".PHP_EOL);
202  }
203 
209  protected function _getShopBasePath()
210  {
211  return getConfig()->getConfigParam( "sShopDir" );
212  }
213 
219  protected function _getImageUri()
220  {
221  if ( $this->_sImageUri === null ) {
222 
223  $this->_sImageUri = "";
224  $sReqPath = "out/pictures/generated";
225 
226 
227  $sReqImg = isset( $_SERVER["REQUEST_URI"] ) ? urldecode($_SERVER["REQUEST_URI"]) : "";
228  if ( ( $iPos = strpos( $sReqImg, $sReqPath ) ) !== false ) {
229  $this->_sImageUri = substr( $sReqImg, $iPos );
230  }
231 
232  $this->_sImageUri = trim( $this->_sImageUri, "/" );
233  }
234 
235  return $this->_sImageUri;
236  }
237 
243  protected function _getImageName()
244  {
245  return basename( $this->_getImageUri() );
246  }
247 
253  protected function _getImageMasterPath()
254  {
255  $sUri = $this->_getImageUri();
256  $sPath = false;
257 
258  if ( $sUri && ( $sPath = dirname( dirname( $sUri ) ) ) ) {
259  $sPath = preg_replace( "/\/([^\/]*)\/([^\/]*)\/([^\/]*)$/", "/master/\\2/\\3/", $sPath );
260  }
261 
262  return $sPath;
263  }
264 
270  protected function _getImageInfo()
271  {
272  $aInfo = array();
273  if ( ( $sUri = $this->_getImageUri() ) ) {
274  $aInfo = explode( $this->_sImageInfoSep, basename( dirname( $sUri ) ) );
275  }
276 
277  return $aInfo;
278  }
279 
285  protected function _getImageTarget()
286  {
287  return $this->_getShopBasePath() . $this->_getImageUri();
288  }
289 
295  protected function _getNopicImageTarget()
296  {
297  $sPath = $this->_getShopBasePath() . $this->_getImageUri();
298  return str_replace( $this->_getImageName(), "nopic.jpg", $sPath );
299  }
300 
306  protected function _getImageType()
307  {
308  $sType = preg_replace( "/.*\.(png|jp(e)?g|gif)$/", "\\1", $this->_getImageName() );
309  $sType = ( strcmp( $sType, "jpg") == 0 ) ? "jpeg" : $sType;
310  return in_array( $sType, $this->_aAllowedImgTypes ) ? $sType : false;
311  }
312 
323  protected function _generatePng( $sSource, $sTarget, $iWidth, $iHeight )
324  {
325  return resizePng( $sSource, $sTarget, $iWidth, $iHeight, @getimagesize( $sSource ), getGdVersion(), null );
326  }
327 
339  protected function _generateJpg( $sSource, $sTarget, $iWidth, $iHeight, $iQuality )
340  {
341  return resizeJpeg( $sSource, $sTarget, $iWidth, $iHeight, @getimagesize( $sSource ), getGdVersion(), null, $iQuality );
342  }
343 
354  protected function _generateGif( $sSource, $sTarget, $iWidth, $iHeight )
355  {
356  $aImageInfo = @getimagesize( $sSource );
357  return resizeGif( $sSource, $sTarget, $iWidth, $iHeight, $aImageInfo[0], $aImageInfo[1], getGdVersion() );
358  }
359 
368  protected function _isTargetPathValid( $sPath )
369  {
370  $blValid = true;
371  $sDir = dirname( trim( $sPath ) );
372 
373  // first time folder access?
374  if ( ! is_dir( $sDir ) && ( $blValid = $this->_isValidPath( $sDir ) ) ) {
375  // creating missing folders
376  $blValid = $this->_createFolders( $sDir );
377  }
378 
379  return $blValid;
380  }
381 
389  protected function _createFolders( $sDir )
390  {
391  $oConfig = getConfig();
392  $sPicFolderPath = dirname( $oConfig->getMasterPictureDir() );
393 
394  $blDone = false;
395  if ( $sPicFolderPath && is_dir( $sPicFolderPath ) ) {
396 
397  // if its in main path..
398  if ( strcmp( $sPicFolderPath, substr( $sDir, 0, strlen( $sPicFolderPath ) ) ) == 0 ) {
399  // folder does not exist yet?
400  if ( ! ( $blDone = file_exists( $sDir ) ) ) {
401  clearstatcache();
402  // in case creation did not succeed, maybe another process allready created folder?
403  $iMode = 0755;
404  if ( defined( 'OXID_PHP_UNIT' ) ) {
405  $iMode = 0777;
406  }
407  $blDone = mkdir( $sDir, $iMode, true ) || file_exists( $sDir );
408  }
409  }
410  }
411 
412  return $blDone;
413  }
414 
422  protected function _isValidPath( $sPath )
423  {
424  $blValid = false;
425 
426  list( $iWidth, $iHeight, $sQuality ) = $this->_getImageInfo();
427  if ( $iWidth && $iHeight && $sQuality ) {
428 
429  $oConfig = getConfig();
431 
432  // parameter names
433  $sNames = '';
434  foreach ( $this->_aConfParamToPath as $sParamName => $sPathReg ) {
435  if ( preg_match( $sPathReg, $sPath ) ) {
436  if ( $sNames ) {
437  $sNames .= ", ";
438  }
439  $sNames .= $oDb->quote( $sParamName );
440 
441  if ( $sParamName == "sManufacturerIconsize" || $sParamName == "sCatIconsize" ) {
442  $sNames .= ", " . $oDb->quote( "sIconsize" );
443  }
444  }
445  }
446 
447  // any name matching path?
448  if ( $sNames ) {
449 
450  $sDecodeField = $oConfig->getDecodeValueQuery();
451 
452  // selecting shop which image quality matches user given
453  $sQ = "select oxshopid from oxconfig where oxvarname = 'sDefaultImageQuality' and
454  {$sDecodeField} = " . $oDb->quote( $sQuality );
455 
456  $aShopIds = $oDb->getAll( $sQ );
457 
458  // building query:
459  // shop id
460  $sShopIds = '';
461  foreach ( $aShopIds as $aShopId ) {
462 
463  // probably here we can resolve and check shop id to shorten check?
464 
465 
466  if ( $sShopIds ) {
467  $sShopIds .= ", ";
468  }
469  $sShopIds .= $oDb->quote( $aShopId["oxshopid"] );
470  }
471 
472  // any shop matching quality
473  if ( $sShopIds ) {
474 
475  //
476  $sCheckSize = "$iWidth*$iHeight";
477 
478  // selecting config variables to check
479  $sQ = "select oxvartype, {$sDecodeField} as oxvarvalue from oxconfig
480  where oxvarname in ( {$sNames} ) and oxshopid in ( {$sShopIds} ) order by oxshopid";
481 
482  $aValues = $oDb->getAll( $sQ );
483  foreach ( $aValues as $aValue ) {
484  $aConfValues = (array) $oConfig->decodeValue( $aValue["oxvartype"], $aValue["oxvarvalue"] );
485  foreach ( $aConfValues as $sValue ) {
486  if ( strcmp( $sCheckSize, $sValue ) == 0 ) {
487  $blValid = true;
488  break;
489  }
490  }
491  }
492  }
493  }
494  }
495  return $blValid;
496  }
497 
506  protected function _generateImage( $sImageSource, $sImageTarget )
507  {
508  $sPath = false;
509 
510  if ( getGdVersion() !== false && $this->_isTargetPathValid( $sImageTarget ) && ( $sImageType = $this->_getImageType() ) ) {
511 
512  // including generator files
513  includeImageUtils();
514 
515  // in case lock file creation failed should check if another process did not created image yet
516  if ( $this->_lock( $sImageTarget ) ) {
517 
518  // extracting image info - size/quality
519  list( $iWidth, $iHeight, $iQuality ) = $this->_getImageInfo();
520  switch ( $sImageType ) {
521  case "png":
522  $sPath = $this->_generatePng( $sImageSource, $sImageTarget, $iWidth, $iHeight );
523  break;
524  case "jpeg":
525  $sPath = $this->_generateJpg( $sImageSource, $sImageTarget, $iWidth, $iHeight, $iQuality );
526  break;
527  case "gif":
528  $sPath = $this->_generateGif( $sImageSource, $sImageTarget, $iWidth, $iHeight );
529  break;
530  }
531 
532  // releasing..
533  if ( $sPath ) {
534  $this->_unlock( $sImageTarget );
535  }
536  } else {
537  // assuming that image was created by another process
538  $sPath = file_exists( $sImageTarget ) ? $sImageTarget : false;
539  }
540  }
541 
542  return $sPath;
543  }
544 
552  protected function _getLockName( $sName )
553  {
554  return "$sName.lck";
555  }
556 
564  protected function _lock( $sSource )
565  {
566  $blLocked = false;
567  $sLockName = $this->_getLockName( $sSource );
568 
569  // creating lock file
570  $this->_hLockHandle = @fopen( $this->_getLockName( $sSource ), "w" );
571  if ( $this->_hLockHandle ) {
572  if ( !( $blLocked = flock( $this->_hLockHandle, LOCK_EX ) ) ) {
573  // on failure - closing
574  fclose( $rHandle );
575  }
576  }
577 
578  // in case system does not support file lockings
579  if ( !$blLocked ) {
580  // start a blank file to inform other processes we are dealing with it.
581  if (!( file_exists( $this->_getLockName( $sSource ) ) && abs( time() - filectime( $this->_getLockName( $sSource ) ) < 40 ) ) ) {
582  if ( $this->_hLockHandle = @fopen( $this->_getLockName( $sSource ), "w" ) ) {
583  $blLocked = true;
584  }
585  }
586  }
587 
588  return $blLocked;
589  }
590 
598  protected function _unlock( $sSource )
599  {
600  if ( $this->_hLockHandle ) {
601  flock( $this->_hLockHandle, LOCK_UN );
602  fclose( $this->_hLockHandle );
603  unlink( $this->_getLockName( $sSource ) );
604  }
605  }
606 
615  public function getImagePath( $sAbsPath = false )
616  {
617  if ( $sAbsPath ) {
618  $this->_sImageUri = str_replace( $this->_getShopBasePath(), "", $sAbsPath );
619  }
620 
621  $sImagePath = false;
622  $sMasterPath = $this->_getImageMasterPath();
623 
624  // building base path + extracting image name + extracting master image path
625  $sMasterImagePath = $this->_getShopBasePath() . $sMasterPath . $this->_getImageName();
626 
627  if ( file_exists( $sMasterImagePath ) ) {
628  $sGenImagePath = $this->_getImageTarget();
629  } else {
630  // nopic master path
631  $sMasterImagePath = $this->_getShopBasePath() . dirname( dirname( $sMasterPath ) ) . "/nopic.jpg";
632  $sGenImagePath = $this->_getNopicImageTarget();
633 
634  // 404 header for nopic
635  $this->_setHeader( "HTTP/1.0 404 Not Found" );
636  }
637 
638  // checking if master image is accessible
639  if ( file_exists( $sGenImagePath ) ) {
640  $sImagePath = $sGenImagePath;
641  } elseif ( file_exists( $sMasterImagePath ) ) {
642  // generating image
643  $sImagePath = $this->_generateImage( $sMasterImagePath, $sGenImagePath );
644  }
645 
646  if ( $sImagePath ) {
647  // image type header
648  $this->_setHeader( "Content-Type: image/" . $this->_getImageType() );
649  } else {
650  // unable to output any file
651  $this->_setHeader( "HTTP/1.0 404 Not Found" );
652  }
653 
654  return $sImagePath;
655  }
656 
664  public function outputImage()
665  {
666  $blBuffer = true;
667  if ( defined( 'OXID_PHP_UNIT' ) ) {
668  $blBuffer = false;
669  }
670 
671  // starting output buffering
672  if ( $blBuffer ) {
673  ob_start();
674  }
675 
676  //
677  $sImgPath = $this->getImagePath();
678 
679  // cleaning extra output
680  if ( $blBuffer ) {
681  ob_clean();
682  }
683 
684  // outputting headers
685  $aHeaders = $this->_getHeaders();
686  foreach ( $aHeaders as $sHeader ) {
687  header( $sHeader );
688  }
689 
690  // sending headers
691  if ( $blBuffer ) {
692  ob_end_flush();
693  }
694 
695  // file is generated?
696  if ( $sImgPath ) {
697  // outputting file
698  @readfile( $sImgPath );
699  }
700  }
701 
709  protected function _setHeader( $sHeader )
710  {
711  $this->_aHeaders[] = $sHeader;
712  }
713 
719  protected function _getHeaders()
720  {
721  return $this->_aHeaders;
722  }
723 }