oxdynimggenerator.php

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