oxdynimggenerator.php

Go to the documentation of this file.
00001 <?php
00002 
00003 // Checks if base path getter does not exist
00004 if ( !function_exists( "getShopBasePath" ) ) {
00010     function getShopBasePath()
00011     {
00012         return realpath( dirname( __FILE__ ) . '/..' ) . '/';
00013     }
00014 }
00015 
00016 
00017 // disables admin
00018 if ( !function_exists( 'isAdmin' )) {
00024     function isAdmin()
00025     {
00026         return false;
00027     }
00028 }
00029 
00030 // starts shop framework and returns config instance
00031 if ( !function_exists( "getConfig" ) ) {
00037     function getConfig()
00038     {
00039         // custom functions file
00040         include_once getShopBasePath() . 'modules/functions.php';
00041 
00042         // Generic utility method file
00043         include_once getShopBasePath() . 'core/oxfunctions.php';
00044 
00045         // initializes singleton config class
00046         return oxConfig::getInstance();
00047     }
00048 }
00049 
00050 // Checks if instance name getter does not exist
00051 if ( !function_exists( "getGeneratorInstanceName" ) ) {
00057     function getGeneratorInstanceName()
00058     {
00059         return "oxdynimggenerator";
00060     }
00061 }
00062 
00063 // checks if GD library version getter does not exist
00064 if ( !function_exists( "getGdVersion" ) ) {
00070     function getGdVersion()
00071     {
00072         static $iVersion = null;
00073 
00074         if ( $iVersion === null ) {
00075             $iVersion = false;
00076             if ( function_exists( "gd_info" ) ) {
00077                 // extracting GD version from php
00078                 $aInfo = gd_info();
00079                 if ( isset( $aInfo["GD Version"] ) ) {
00080                     $iVersion = version_compare( preg_replace( "/[^0-9\.]/", "", $aInfo["GD Version"] ), 1, '>' ) ? 2 : 1;
00081                 }
00082             }
00083 
00084         }
00085         return $iVersion;
00086     }
00087 }
00088 
00089 // checks if image utils file loader does not exist
00090 if ( !function_exists( "includeImageUtils" ) ) {
00096     function includeImageUtils()
00097     {
00098         include_once getShopBasePath() . "core/utils/oxpicgenerator.php";
00099     }
00100 }
00101 
00105 class oxDynImgGenerator
00106 {
00111     protected static $_oInstance = null;
00112 
00117     protected $_aHeaders = array();
00118 
00123     protected $_aAllowedImgTypes = array( "jpg", "jpeg", "png", "gif" );
00124 
00130     protected $_sImageInfoSep = "_";
00131 
00136     protected $_hLockHandle = null;
00137 
00142     protected $_sImageUri = null;
00143 
00148     protected $_aConfParamToPath = array( // ** product
00149                                           "sIconsize"             => '/.*\/generated\/product\/(icon|\d+)\/\d+\_\d+\_\d+$/',  // Icon size
00150                                           "sThumbnailsize"        => '/.*\/generated\/product\/(thumb|\d+)\/\d+\_\d+\_\d+$/', // Thumbnail size
00151                                           "sZoomImageSize"        => '/.*\/generated\/product\/\d+\/\d+\_\d+\_\d+$/',         // Zoom picture size
00152                                           "aDetailImageSizes"     => '/.*\/generated\/product\/\d+\/\d+\_\d+\_\d+$/',         // Product picture size
00153 
00154                                           // ** manufacturer/vendor
00155                                           "sManufacturerIconsize" => '/.*\/generated\/(manufacturer|vendor)\/icon\/\d+\_\d+\_\d+$/', // Manufacturer's|brand logo size
00156 
00157                                           // ** category
00158                                           "sCatThumbnailsize"     => '/.*\/generated\/category\/thumb\/\d+\_\d+\_\d+$/',     // Category picture size
00159                                           "sCatIconsize"          => '/.*\/generated\/category\/icon\/\d+\_\d+\_\d+$/',      // Size of a subcategory's picture
00160                                           "sCatPromotionsize"     => '/.*\/generated\/category\/promo_icon\/\d+\_\d+\_\d+$/' // Category picture size for promotion on startpage
00161                                         );
00162 
00168     public static function getInstance()
00169     {
00170         if ( self::$_oInstance === null ) {
00171             $sInstanceName = getGeneratorInstanceName();
00172             self::$_oInstance = new $sInstanceName();
00173         }
00174         return self::$_oInstance;
00175     }
00176 
00188     public function __call( $sMethod, $aArgs )
00189     {
00190         if ( defined( 'OXID_PHP_UNIT' ) ) {
00191             if ( substr( $sMethod, 0, 4) == "UNIT" ) {
00192                 $sMethod = str_replace( "UNIT", "_", $sMethod );
00193             }
00194             if ( method_exists( $this, $sMethod)) {
00195                 return call_user_func_array( array( & $this, $sMethod ), $aArgs );
00196             }
00197         }
00198 
00199         throw new oxSystemComponentException( "Function '$sMethod' does not exist or is not accessible! (" . get_class($this) . ")".PHP_EOL);
00200     }
00201 
00207     protected function _getShopBasePath()
00208     {
00209         return getConfig()->getConfigParam( "sShopDir" );
00210     }
00211 
00217     protected function _getImageUri()
00218     {
00219         if ( $this->_sImageUri === null ) {
00220 
00221             $this->_sImageUri = "";
00222             $sReqPath = "out/pictures/generated";
00223 
00224 
00225             $sReqImg = isset( $_SERVER["REQUEST_URI"] ) ? $_SERVER["REQUEST_URI"] : "";
00226             if ( ( $iPos = strpos( $sReqImg, $sReqPath ) ) !== false ) {
00227                 $this->_sImageUri = substr( $sReqImg, $iPos );
00228             }
00229 
00230             $this->_sImageUri = trim( $this->_sImageUri, "/" );
00231         }
00232 
00233         return $this->_sImageUri;
00234     }
00235 
00241     protected function _getImageName()
00242     {
00243         return basename( $this->_getImageUri() );
00244     }
00245 
00251     protected function _getImageMasterPath()
00252     {
00253         $sUri  = $this->_getImageUri();
00254         $sPath = false;
00255 
00256         if ( $sUri && ( $sPath = dirname( dirname( $sUri ) ) ) ) {
00257             $sPath = preg_replace( "/\/([^\/]*)\/([^\/]*)\/([^\/]*)$/", "/master/\\2/\\3/", $sPath );
00258         }
00259 
00260         return $sPath;
00261     }
00262 
00268     protected function _getImageInfo()
00269     {
00270         $aInfo = array();
00271         if ( ( $sUri = $this->_getImageUri() ) ) {
00272             $aInfo = explode( $this->_sImageInfoSep, basename( dirname( $sUri ) ) );
00273         }
00274 
00275         return $aInfo;
00276     }
00277 
00283     protected function _getImageTarget()
00284     {
00285         return $this->_getShopBasePath() . $this->_getImageUri();
00286     }
00287 
00293     protected function _getNopicImageTarget()
00294     {
00295         $sPath = $this->_getShopBasePath() . $this->_getImageUri();
00296         return str_replace( $this->_getImageName(), "nopic.jpg", $sPath );
00297     }
00298 
00304     protected function _getImageType()
00305     {
00306         $sType = preg_replace( "/.*\.(png|jp(e)?g|gif)$/", "\\1", $this->_getImageName() );
00307         $sType = ( strcmp( $sType, "jpg") == 0 ) ? "jpeg" : $sType;
00308         return in_array( $sType, $this->_aAllowedImgTypes ) ? $sType : false;
00309     }
00310 
00321     protected function _generatePng( $sSource, $sTarget, $iWidth, $iHeight )
00322     {
00323         return resizePng( $sSource, $sTarget, $iWidth, $iHeight, @getimagesize( $sSource ), getGdVersion(), null );
00324     }
00325 
00337     protected function _generateJpg( $sSource, $sTarget, $iWidth, $iHeight, $iQuality )
00338     {
00339         return resizeJpeg( $sSource, $sTarget, $iWidth, $iHeight, @getimagesize( $sSource ), getGdVersion(), null, $iQuality );
00340     }
00341 
00352     protected function _generateGif( $sSource, $sTarget, $iWidth, $iHeight )
00353     {
00354         $aImageInfo = @getimagesize( $sSource );
00355         return resizeGif( $sSource, $sTarget, $iWidth, $iHeight, $aImageInfo[0], $aImageInfo[1], getGdVersion() );
00356     }
00357 
00366     protected function _isTargetPathValid( $sPath )
00367     {
00368         $blValid = true;
00369         $sDir = dirname( trim( $sPath ) );
00370 
00371         // first time folder access?
00372         if ( ! is_dir( $sDir ) && ( $blValid = $this->_isValidPath( $sDir ) ) ) {
00373             // creating missing folders
00374             $blValid = $this->_createFolders( $sDir );
00375         }
00376 
00377         return $blValid;
00378     }
00379 
00387     protected function _createFolders( $sDir )
00388     {
00389         $oConfig = getConfig();
00390         $sPicFolderPath = dirname( $oConfig->getMasterPictureDir() );
00391 
00392         $blDone = false;
00393         if ( $sPicFolderPath && is_dir( $sPicFolderPath ) ) {
00394 
00395             // if its in main path..
00396             if ( strcmp( $sPicFolderPath, substr( $sDir, 0, strlen( $sPicFolderPath ) ) ) == 0 ) {
00397                 // folder does not exist yet?
00398                 if ( ! ( $blDone = file_exists( $sDir ) ) ) {
00399                     clearstatcache();
00400                     // in case creation did not succeed, maybe another process allready created folder?
00401                     $iMode = 0755;
00402                     if ( defined( 'OXID_PHP_UNIT' ) ) {
00403                         $iMode = 0777;
00404                     }
00405                     $blDone = mkdir( $sDir, $iMode, true ) || file_exists( $sDir );
00406                 }
00407             }
00408         }
00409 
00410         return $blDone;
00411     }
00412 
00420     protected function _isValidPath( $sPath )
00421     {
00422         $blValid = false;
00423 
00424         list( $iWidth, $iHeight, $sQuality ) = $this->_getImageInfo();
00425         if ( $iWidth && $iHeight && $sQuality ) {
00426 
00427             $oConfig = getConfig();
00428             $oDb = oxDb::getDb(true);
00429 
00430             // parameter names
00431             $sNames = '';
00432             foreach ( $this->_aConfParamToPath as $sParamName => $sPathReg ) {
00433                 if ( preg_match( $sPathReg, $sPath ) ) {
00434                     if ( $sNames ) {
00435                         $sNames .= ", ";
00436                     }
00437                     $sNames .= $oDb->quote( $sParamName );
00438 
00439                     if ( $sParamName == "sManufacturerIconsize" || $sParamName == "sCatIconsize" ) {
00440                         $sNames .= ", " . $oDb->quote( "sIconsize" );
00441                     }
00442                 }
00443             }
00444 
00445             // any name matching path?
00446             if ( $sNames ) {
00447 
00448                 $sDecodeField = $oConfig->getDecodeValueQuery();
00449 
00450                 // selecting shop which image quality matches user given
00451                 $sQ = "select oxshopid from oxconfig where oxvarname = 'sDefaultImageQuality' and
00452                        {$sDecodeField} = " . $oDb->quote( $sQuality );
00453 
00454                 $aShopIds = $oDb->getAll( $sQ );
00455 
00456                 // building query:
00457                 // shop id
00458                 $sShopIds = '';
00459                 foreach ( $aShopIds as $aShopId ) {
00460 
00461                     // probably here we can resolve and check shop id to shorten check?
00462 
00463 
00464                     if ( $sShopIds ) {
00465                         $sShopIds .= ", ";
00466                     }
00467                     $sShopIds .= $oDb->quote( $aShopId["oxshopid"] );
00468                 }
00469 
00470                 // any shop matching quality
00471                 if ( $sShopIds ) {
00472 
00473                     //
00474                     $sCheckSize = "$iWidth*$iHeight";
00475 
00476                     // selecting config variables to check
00477                     $sQ = "select oxvartype, {$sDecodeField} as oxvarvalue from oxconfig
00478                            where oxvarname in ( {$sNames} ) and oxshopid in ( {$sShopIds} ) order by oxshopid";
00479 
00480                     $aValues = $oDb->getAll( $sQ );
00481                     foreach ( $aValues as $aValue ) {
00482                         $aConfValues = (array) $oConfig->decodeValue( $aValue["oxvartype"], $aValue["oxvarvalue"] );
00483                         foreach ( $aConfValues as $sValue ) {
00484                             if ( strcmp( $sCheckSize, $sValue ) == 0 ) {
00485                                 $blValid = true;
00486                                 break;
00487                             }
00488                         }
00489                     }
00490                 }
00491             }
00492         }
00493         return $blValid;
00494     }
00495 
00504     protected function _generateImage( $sImageSource, $sImageTarget )
00505     {
00506         $sPath = false;
00507 
00508         if ( getGdVersion() !== false && $this->_isTargetPathValid( $sImageTarget ) && ( $sImageType = $this->_getImageType() ) ) {
00509 
00510             // including generator files
00511             includeImageUtils();
00512 
00513             // in case lock file creation failed should check if another process did not created image yet
00514             if ( $this->_lock( $sImageTarget ) ) {
00515 
00516                 // extracting image info - size/quality
00517                 list( $iWidth, $iHeight, $iQuality ) = $this->_getImageInfo();
00518                 switch ( $sImageType ) {
00519                     case "png":
00520                         $sPath = $this->_generatePng( $sImageSource, $sImageTarget, $iWidth, $iHeight );
00521                         break;
00522                     case "jpeg":
00523                         $sPath = $this->_generateJpg( $sImageSource, $sImageTarget, $iWidth, $iHeight, $iQuality );
00524                         break;
00525                     case "gif":
00526                         $sPath = $this->_generateGif( $sImageSource, $sImageTarget, $iWidth, $iHeight );
00527                         break;
00528                 }
00529 
00530                 // releasing..
00531                 if ( $sPath ) {
00532                     $this->_unlock( $sImageTarget );
00533                 }
00534             } else {
00535                 // assuming that image was created by another process
00536                 $sPath = file_exists( $sImageTarget ) ? $sImageTarget : false;
00537             }
00538         }
00539 
00540         return $sPath;
00541     }
00542 
00550     protected function _getLockName( $sName )
00551     {
00552         return "$sName.lck";
00553     }
00554 
00562     protected function _lock( $sSource )
00563     {
00564         $blLocked  = false;
00565         $sLockName = $this->_getLockName( $sSource );
00566 
00567         // creating lock file
00568         $this->_hLockHandle = @fopen( $this->_getLockName( $sSource ), "w" );
00569         if ( $this->_hLockHandle ) {
00570              if ( !( $blLocked = flock( $this->_hLockHandle, LOCK_EX ) ) ) {
00571                 // on failure - closing
00572                  fclose( $rHandle );
00573              }
00574         }
00575 
00576         // in case system does not support file lockings
00577         if ( !$blLocked ) {
00578             // start a blank file to inform other processes we are dealing with it.
00579             if (!( file_exists( $this->_getLockName( $sSource ) ) && abs( time() - filectime( $this->_getLockName( $sSource ) ) < 40 ) ) ) {
00580                 if ( $this->_hLockHandle = @fopen( $this->_getLockName( $sSource ), "w" ) ) {
00581                     $blLocked = true;
00582                 }
00583             }
00584         }
00585 
00586         return $blLocked;
00587     }
00588 
00596     protected function _unlock( $sSource )
00597     {
00598         if ( $this->_hLockHandle ) {
00599             flock( $this->_hLockHandle, LOCK_UN );
00600             fclose( $this->_hLockHandle );
00601             unlink( $this->_getLockName( $sSource ) );
00602         }
00603     }
00604 
00613     public function getImagePath( $sAbsPath = false )
00614     {
00615         if ( $sAbsPath ) {
00616             $this->_sImageUri = str_replace( $this->_getShopBasePath(), "", $sAbsPath );
00617         }
00618 
00619         $sImagePath  = false;
00620         $sMasterPath = $this->_getImageMasterPath();
00621 
00622         // building base path + extracting image name + extracting master image path
00623         $sMasterImagePath = $this->_getShopBasePath() . $sMasterPath . $this->_getImageName();
00624 
00625         if ( file_exists( $sMasterImagePath ) ) {
00626             $sGenImagePath = $this->_getImageTarget();
00627         } else {
00628             // nopic master path
00629             $sMasterImagePath = $this->_getShopBasePath() . dirname( dirname( $sMasterPath ) ) . "/nopic.jpg";
00630             $sGenImagePath    = $this->_getNopicImageTarget();
00631 
00632             // 404 header for nopic
00633             $this->_setHeader( "HTTP/1.0 404 Not Found" );
00634         }
00635 
00636         // checking if master image is accessible
00637         if ( file_exists( $sGenImagePath ) ) {
00638             $sImagePath = $sGenImagePath;
00639         } elseif ( file_exists( $sMasterImagePath ) ) {
00640             // generating image
00641             $sImagePath = $this->_generateImage( $sMasterImagePath, $sGenImagePath );
00642         }
00643 
00644         if ( $sImagePath ) {
00645             // image type header
00646             $this->_setHeader( "Content-Type: image/" . $this->_getImageType() );
00647         } else {
00648             // unable to output any file
00649             $this->_setHeader( "HTTP/1.0 404 Not Found" );
00650         }
00651 
00652         return $sImagePath;
00653     }
00654 
00662     public function outputImage()
00663     {
00664         $blBuffer = true;
00665         if ( defined( 'OXID_PHP_UNIT' ) ) {
00666            $blBuffer = false;
00667         }
00668 
00669         // starting output buffering
00670         if ( $blBuffer ) {
00671            ob_start();
00672         }
00673 
00674         //
00675         $sImgPath = $this->getImagePath();
00676 
00677         // cleaning extra output
00678         if ( $blBuffer ) {
00679             ob_clean();
00680         }
00681 
00682         // outputting headers
00683         $aHeaders = $this->_getHeaders();
00684         foreach ( $aHeaders as $sHeader ) {
00685             header( $sHeader );
00686         }
00687 
00688         // sending headers
00689         if ( $blBuffer ) {
00690             ob_end_flush();
00691         }
00692 
00693         // file is generated?
00694         if ( $sImgPath ) {
00695             // outputting file
00696             @readfile( $sImgPath );
00697         }
00698     }
00699 
00707     protected function _setHeader( $sHeader )
00708     {
00709         $this->_aHeaders[] = $sHeader;
00710     }
00711 
00717     protected function _getHeaders()
00718     {
00719         return $this->_aHeaders;
00720     }
00721 }