array( '_libs', '_did', '.svn', '.cvs' ) , 'allowedFileExtensions' => array( 'jpg', 'png', 'gif' ) , 'redirectToContent' => true //if false, outputs the log, else it redirects to the image , 'debug' => false //shows debug log (also disabled if redirectToContent is enabled , 'stopGeneration' => false //only for debug obv //enabling this can lead to damage, accidental deletion of hard disk. Beware ! // when disabled it makes php die() when any error (notices or greater) happens. //this is because an simple mistype when working with files could lead a filesystem path's variable //to become empty and make the class reference root directory. //a better solution is (of course) well set permissions, btw this is not always possible for //the average (windows-based) developer , 'continueOnError' => false , 'driversPath' => 'controllers' ); function CacheOnRequest( $globalDebug = false ) { if ( $globalDebug === false || $globalDebug === 0 ) { $this->configuration['debug']=false; $this->configuration['stopGeneration']=false; } if ( $globalDebug === 1 ) //shows problems on error { $this->configuration['debug']=true; $this->configuration['stopGeneration']=false; } if ( $globalDebug === 2 || $globalDebug === true ) //shows logs anyway, hinibits file generation { $this->configuration['debug']=true; $this->configuration['stopGeneration']=true; $this->configuration['redirectToContent']=false; } $this->_autoIncludeDrivers(); } function run ( $uri, $basePath ) { ob_start(); $this->_debugInit(); $this->uri = $uri; unset ( $uri ); $this->basePath = $basePath; $DocumentRoot = new Path( $_SERVER[ 'DOCUMENT_ROOT' ], null ); $this->documentRoot = $DocumentRoot->normalized(); //for retrieving source lll ( 'current uri: '. $this->uri ); lll ( 'current basePath: '. $this->basePath ); lll ( 'current documentRoot: '. $this->documentRoot ); lll ( 'current configuration', $this->configuration ); //------- analyzing $this->analyzeRequest( $this->uri ); $result = $this->runDrivers( $this->tokens ); ob_end_flush(); } function runDrivers ( $tokens ) { foreach ( array_keys( $this->drivers) as $key ) { $Driver =& $this->drivers[ $key ]; $result = $Driver->run( $tokens ); if ( $result === true ) { lll ( 'Driver '.get_class( $Driver ).' accepted and completed generation request.'); break; } if ( is_null( $result )) { lll ( 'Driver '.get_class( $Driver ).' skipped or processed only part of request.'); } if ( is_array( $result )) { $tokens = $result; lll ( 'Driver '.get_class( $Driver ).' processed part of request and updated URI to: '.$this->_tokens2path( $tokens )); } if ( $result === false ) { lll ( 'Driver '.get_class( $Driver ).' asked stop of all processing. Aborting.'); break; } } } function analyzeRequest ( $uri ) { lll ( '---- analyzing '.$uri ); $this->tokens = $this->_getTokens( $uri ); lll( 'request tokens:', $this->tokens ); } function _tokensDiff ( $tokens, $base ) { foreach ( $base as $baseToken ) { if ( array_shift( $tokens ) !== $baseToken ) return false; } return $tokens; } function _getTokens ( $uri ) { $myBase = $this->_path2tokens( dirname( $_SERVER[ 'PHP_SELF' ] )); $tokens = $this->_path2tokens( $uri ); return $this->_tokensDiff( $tokens, $myBase ); } function _tokens2path ( $tokens ) { return join( '/', $tokens ); } function _path2tokens ( $uri ) { $parsedUri = parse_url( $uri ); $uri = $parsedUri[ 'path' ]; $uri = trim( $uri, '/' ); $tokens = explode( '/', $uri ); return $tokens; } function _debugInit() { if ( $this->configuration[ 'debug' ] ) Logger::printScreen( true ); //comment out to disable screen logging Logger::progressiveFlush( false ); //avoid problem when sending headers if ( ! $this->configuration[ 'continueOnError' ] ) { set_error_handler( array( $this , 'errorHandler')); } } //---------------- drivers function _autoIncludeDrivers () { $driversPath = $this->_normalizePathToAbsolute( $this->configuration[ 'driversPath' ], dirname( __FILE__ ) ); if ( ! file_exists($driversPath ) ) { trigger_error ( "driver path '{$this->configuration[ 'driversPath' ]}' ('$driversPath') not found, aborting "); die; } $driversFiles = $this->_fileList( $driversPath ); asort( $driversFiles ); foreach ( $driversFiles as $file ) include_once ( $file ); } function attachDriver ( &$Driver ) { // $this->driver = new ImageResizer (); $Driver->setController( &$this ); $this->drivers[] =& $Driver; } //---------- errorhandling function errorHandler ( $errno, $errstr, $errfile, $errline ) { if ( $errno > E_ALL ) return false; if ( !$errno ) return false; $codes = array( E_NOTICE => 'E_NOTICE', E_WARNING => 'E_WARNING', E_ERROR => 'E_ERROR', E_USER_ERROR => 'E_USER_ERROR', E_USER_WARNING => 'E_USER_WARNING', E_USER_NOTICE => 'E_USER_NOTICE' ); if ( key_exists( $errno, $codes )) $errno = $codes[ $errno ]; $args = array( $errno, 'message' => $errstr, 'file' => $errfile, 'line' =>$errline); if ( error_reporting() === 0 ) { lll ( 'Error on a @-prepended function. Continuing. Error details:.',$args ); return false; } lll ( 'Error Caught ! Aborting for security purpouses.',$args ); ob_flush(); die; } //---------- tokens and files handling --------- function tokens2SourceFilename( $tokens ) { $Path = $this->_getPathObj( join( '/', $tokens ), $this->documentRoot ); //cabled return $Path->absolute(); } function tokens2DestinationFilename( $tokens ) { $Path = $this->_getPathObj( join( '/', $tokens ), $this->basePath ); return $Path->absolute(); } //---------- filesystem utils function _normalizePathToAbsolute ( $relativePath, $basePath = false ) { $Path = $this->_getPathObj( $relativePath, $basePath ); return $Path->absolute(); } function _normalizePath( $relativePath ) { $Path = $this->_getPathObj( $relativePath ); return $Path->normalized(); } function _fileList( $path ) { $entries = $this->_pathEntries( $path ); if ( $entries === false ) return false; foreach ( $entries as $k => $entry ) { if ( !is_file( $entry )) unset( $entries[ $k ] ); } return $entries; } function _dirList( $path ) { $entries = $this->_pathEntries( $path ); if ( $entries === false ) return false; foreach ( $entries as $k => $entry ) { if ( !is_dir( $entry )) unset( $entries[ $k ] ); } return $entries; } function _pathEntries ( $path ) { $path = $this->_normalizePath( $path ); $entries = array(); if ( ! file_exists( $path )) { lll ( 'reading attempt at path: "'.$path.'" failed. Path doesn\'t exists' ); return false; } $CurrentDir = dir( $path ); while (false !== ($entry = $CurrentDir->read())) { $fullEntryName = $path.DIRECTORY_SEPARATOR.$entry; if ( $entry != '..' && $entry != '.' ) { $entries[] = $fullEntryName; if ( is_dir( $fullEntryName ) ) { } elseif ( is_file( $fullEntryName )) { } } else { // $this->log( $fullEntryName.' [r]' ); } } $CurrentDir->close(); return $entries; } function _getPathObj ( $path, $basePath = false) { if ( $basePath === false ) $basePath = $this->basePath; return new Path( $path, $basePath ); } } /** * * Sample usage * * Copy this package into /dynamic directory of your website.
* write a in the browser http://[site.com]/dynamic/320x200/yourimagefolder/yourimagesubfolder/image.jpg * after that... refresh again. * * NOTES: * - overwrite renderImage() function to implement our own resize handling * - if DIRECTORY_SEPARATOR is not defined we will attempt to set it. * * @todo * Known problems: * - using more parameters * - staticalling images from many folders with the same name * workaround .. ? * - symlinks anywhere along document root path make everything fall. * To implement: * - reset mechanism for the singleimage. via the /reset/ param */ class GenerateOnRequest { var $configuration = array( 'excludeDirs' => array( '_libs', '_did', '.svn', '.cvs' ) , 'allowedFileExtensions' => array( 'jpg', 'png', 'gif' ) , 'redirectToContent' => true //if false, outputs the log, else it redirects to the image , 'debug' => false //shows debug log (also disabled if redirectToContent is enabled , 'stopGeneration' => false //only for debug obv //enabling this can lead to damage, accidental deletion of hard disk. Beware ! // when disabled it makes php die() when any error (notices or greater) happens. //this is because an simple mistype when working with files could lead a filesystem path's variable //to become empty and make the class reference root directory. //a better solution is (of course) well set permissions, btw this is not always possible for //the average (windows-based) developer , 'continueOnError' => false , 'driversPath' => 'drivers' ); var $uri = null; //uri requested var $basePath = null; //base path of generated images var $vars = array(); var $ref = array(); var $drivers = array(); //stack of attached controllers /** * Enter description here... * * @param mixed $globalDebug 0/false doesn't show anything, (int)1 shows log on error, 2/true hinibits filegeneration * @return GenerateOnRequest */ function GenerateOnRequest( $globalDebug = false ) { if ( $globalDebug === false || $globalDebug === 0 ) { $this->configuration['debug']=false; $this->configuration['stopGeneration']=false; } if ( $globalDebug === 1 ) //shows problems on error { $this->configuration['debug']=true; $this->configuration['stopGeneration']=false; } if ( $globalDebug === 2 || $globalDebug === true ) //shows logs anyway, hinibits file generation { $this->configuration['debug']=true; $this->configuration['stopGeneration']=true; $this->configuration['redirectToContent']=false; } $this->_autoIncludeDrivers(); } function _autoIncludeDrivers () { $driversPath = $this->_normalizePathToAbsolute( $this->configuration[ 'driversPath' ], dirname( __FILE__ ) ); $driversFiles = $this->_fileList( $driversPath ); asort( $driversFiles ); foreach ( $driversFiles as $file ) include_once ( $file ); } function attachDriver ( &$Driver ) { // $this->driver = new ImageResizer (); $Driver->setController( &$this ); $this->drivers[] =& $Driver; } function errorHandler ( $errno, $errstr, $errfile, $errline ) { if ( $errno > E_ALL ) return false; if ( !$errno ) return false; $codes = array( E_NOTICE => 'E_NOTICE', E_WARNING => 'E_WARNING', E_ERROR => 'E_ERROR', E_USER_ERROR => 'E_USER_ERROR', E_USER_WARNING => 'E_USER_WARNING', E_USER_NOTICE => 'E_USER_NOTICE' ); if ( key_exists( $errno, $codes )) $errno = $codes[ $errno ]; $args = array( $errno, 'message' => $errstr, 'file' => $errfile, 'line' =>$errline); if ( error_reporting() === 0 ) { lll ( 'Error on a @-prepended function. Continuing. Error details:.',$args ); return false; } lll ( 'Error Caught ! Aborting for security purpouses.',$args ); ob_flush(); die; } function _normalizeExclusionList () { foreach ( $this->configuration[ 'excludeDirs' ] as $k => $dir ) { $dir = $this->_normalizePathToAbsolute( $dir ); $this->configuration[ 'excludeDirs' ][ $k ] = $dir; } // lll( 'list of current excluded dirs:', $this->configuration[ 'excludeDirs' ] ); } /** * Exists from the flow with a die(); */ function _permissionDenied() { die( 'permission denied' ); } /** * Process the request. * * This methos will try to generate the requested file and then attempt to redirect user to the generated file. * * @param string $uri request uri * @param string $basePath base path where to put generated images. Must be writable from php */ function run ( $uri, $basePath ) { ob_start(); if ( $this->configuration[ 'debug' ] ) Logger::printScreen( true ); //comment out to disable screen logging Logger::progressiveFlush( false ); //avoid problem when sending headers if ( ! $this->configuration[ 'continueOnError' ] ) { set_error_handler( array( $this , 'errorHandler')); } $this->uri = $uri; unset ( $uri ); $this->basePath = $basePath; $this->_normalizeExclusionList(); lll ( 'current configuration', $this->configuration ); // ----------- PHP COMPAT if ( ! defined( 'DIRECTORY_SEPARATOR' )) define( DIRECTORY_SEPARATOR, strtoupper(substr(PHP_OS,0,3)=='WIN')?'\\':'/' ); //-------------INCLUSIONS //let's override error document's 404 response header("HTTP/1.0 200 OK"); if ( strpos ( $this->uri, '?again' ) ) //loops prevention { $this->uri = str_replace( '?again', '', $this->uri); $this->configuration[ 'redirectToContent' ] = false; } $this->vars = $this->_urlProcess(); lll ( 'DIRECTORY_SEPARATOR', DIRECTORY_SEPARATOR ); lll ( 'sniffed vars', $this->vars ); if ( ! $this->_securityCheck()) { $this->_permissionDenied(); } $pathCreated = $this->createPath ( $this->basePath, $this->vars[ 'neededPath' ] ); lll ( 'path created? '. $pathCreated ); $imagePath = $this->basePath; foreach ( array_keys( $this->drivers) as $key ) { $Driver =& $this->drivers[ $key ]; $generated = $Driver->process( $this->vars[ 'sourceFilename' ], $this->vars[ 'destinationFilename' ], $this->vars[ 'params' ] ); if ( $generated ) { lll ( 'Driver '.get_class( $Driver ).' accepted and completed generation request.'); break; } if ( is_null( $generated )) { lll ( 'Driver '.get_class( $Driver ).' skipped or processed only part of request.'); } if ( $generated === false ) { lll ( 'Driver '.get_class( $Driver ).' asked stop of all processing. Aborting.'); break; } } if ( $generated && $this->configuration[ 'redirectToContent' ] ) { header( 'Location: '.$_SERVER[ 'REQUEST_URI' ].'?again' ); //lll( 'redirecting to the image:'.$_SERVER[ 'REQUEST_URI' ]); ob_flush(); ob_clean(); } else { lll ( 'no redirection. end of process.'); ob_get_flush(); } } /** * This will parse current uri and try to sniff everything possible (source-name, destination-name etc) * * Everything will be path into $this->vars * * @return unknown */ function _urlProcess () { $path =$this->uri; if ( strpos( $path, $this->basePath ) === 0 ) str_replace( $this->basePath , '', $path); $path = $this->_normalizePath( $path ); $documentRoot = $this->_normalizePath( $_SERVER[ 'DOCUMENT_ROOT' ] ); $pathFromRoot = str_replace($documentRoot, '', $this->basePath ); $query = str_replace( $pathFromRoot, '', $path ); $query = ltrim( $query, DIRECTORY_SEPARATOR ); $params = explode( DIRECTORY_SEPARATOR, $query ); $fileName = array_pop( $params ); $neededPath = join( DIRECTORY_SEPARATOR, $params ); //------------ source filename $sourceFilename = $params; array_shift( $sourceFilename ); //first parameter is the actual command $sourceFilename = $documentRoot . DIRECTORY_SEPARATOR . join( DIRECTORY_SEPARATOR, $sourceFilename ) . DIRECTORY_SEPARATOR . $fileName; $sourceFileExists = file_exists( $sourceFilename ); $destinationPath =$this->basePath. DIRECTORY_SEPARATOR. join ( DIRECTORY_SEPARATOR, $params ); $destinationFilename = $destinationPath. DIRECTORY_SEPARATOR .$fileName; //for clearness /* needed - destinationFile (precise) - basePath - action tokens: diffed from (/dynamic) base URL - destinationBasePath (should concide with base URL) - sourceFIle - needs a base ( documentRoot ) */ $DocumentRoot = $this->_getPathObj( $_SERVER[ 'DOCUMENT_ROOT' ], $this->basePath ); $DestinationFilename = $this->_getPathObj( join ( DIRECTORY_SEPARATOR, $params ), $this->basePath ); $SourceFilename = $this->_getPathObj( $this->uri ); $SourceFilename->path = $SourceFilename->merge( $DocumentRoot->absolute() ); foreach ( get_defined_vars() as $k => $u ) { if ( is_object( $$k )) { $this->ref[ $k ] = $u; unset ( $$k ); } } unset ( $destinationPath, $query, $pathFromRoot ); unset( $k, $u ); lll ( 'references:', $this->ref ); return get_defined_vars(); } //-------------------------------- FUNCTIONS function isExcludedDir ( $fileName ) { $File = $this->_getPathObj( $fileName ); foreach ( $this->configuration[ 'excludeDirs' ] as $excludedPath ) { if ( $File->isInside( $excludedPath) ) { lll( 'Called dir "'. $excludedPath .'" is in the exclusion list.'); return true; } } return false; /* foreach ( $this->configuration[ 'excludeDirs' ] as $excluded ) { if ( substr( $fileName, 0, strlen( $excluded )) == $excluded ) { lll( 'Called dir "'. $dirName .'" is in the exclusion list.'); return true; } } if ( in_array( $dirName , $this->configuration[ 'excludeDirs' ])) { lll( 'Called dir "'. $dirName .'" is in the exclusion list.'); } return false; */ } function _securityCheck ( ) { // hinibits access to libs directorie if ( $this->isExcludedDir( $this->vars[ 'destinationFilename' ] ) ) return false; if ( $this->isExcludedDir( $this->vars[ 'sourceFilename' ] ) ) return false; //$fileExtension = $this->_getFileExtension( $this->vars[ 'fileName' ] ); $Source = $this->_getPathObj( $this->vars[ 'sourceFilename' ] ); $fileExtension = $Source->extension(); if ( ! $fileExtension || ! in_array( $fileExtension , $this->configuration[ 'allowedFileExtensions' ])) { lll( 'Filename "'. $this->vars[ 'fileName' ] .'" must be not-empty, must have an extension and extension has to be inside the allowedFileExtensions list.'); return false; } return true; } function _firstToken ( $haystack, $separator ) { $tokens = explode ( $separator, $haystack ); return array_shift( $tokens ); } //---------------------------- FILESYSTEM FUNCTIONS function _getPathObj ( $path ) { return new Path( $path, $this->basePath ); } // Needs DIRECTORY SEPARATOR already defined function createPath ( $basePath, $path ) { if ( $this->configuration['stopGeneration'] ) { lll( "Simulating creation of '$path' ( basepath '$basePath' )"); return true; } $basePath = rtrim( $basePath, DIRECTORY_SEPARATOR ). DIRECTORY_SEPARATOR; $path = ltrim( $path, DIRECTORY_SEPARATOR ); $tokens = explode( DIRECTORY_SEPARATOR, $path ); $dirname = array_shift( $tokens ); $created = null; $newDirectory = $basePath.$dirname; if ( ! is_dir( $newDirectory )) { $created = mkdir( $newDirectory ); lll ( 'creating "'.$newDirectory.'": '.$created ); } else { lll ( 'skipping "'.$newDirectory.'"'); } if ( $created === false ) { lll ( 'Could not create '.$newDirectory ); return false; } if ( !count( $tokens )) { lll ( 'Final path created: '.$newDirectory ); return true; } return $this->createPath ( $newDirectory, join( DIRECTORY_SEPARATOR, $tokens ) ); } function _getFileExtension ( $filename ) { $tokens = explode( '.', $filename ); if ( count( $tokens ) < 2 ) return null; return array_pop( $tokens ); } function _normalizePath ( $path ) { if ( DIRECTORY_SEPARATOR != '/' ) $path = str_replace( '/', DIRECTORY_SEPARATOR, $path ); $path = rtrim( $path ); return $path; } function _normalizePathToAbsolute ( $path, $basePath = false ) { if ( $basePath === false ) $basePath = $this->basePath; $path = $this->_normalizePath( $path ); if ( substr( $path, 0, 1 ) != DIRECTORY_SEPARATOR ) { //relative path, we assume relative to our base directory $path = $basePath.DIRECTORY_SEPARATOR.$path; } return $path; } function _fileList( $path ) { $entries = $this->_pathEntries( $path ); if ( $entries === false ) return false; foreach ( $entries as $k => $entry ) { if ( !is_file( $entry )) unset( $entries[ $k ] ); } return $entries; } function _dirList( $path ) { $entries = $this->_pathEntries( $path ); if ( $entries === false ) return false; foreach ( $entries as $k => $entry ) { if ( !is_dir( $entry )) unset( $entries[ $k ] ); } return $entries; } function _pathEntries ( $path ) { $path = $this->_normalizePath( $path ); $entries = array(); if ( ! file_exists( $path )) { lll ( 'reading attempt at path: "'.$path.'" failed. Path doesn\'t exists' ); return false; } $CurrentDir = dir( $path ); while (false !== ($entry = $CurrentDir->read())) { $fullEntryName = $path.DIRECTORY_SEPARATOR.$entry; if ( $entry != '..' && $entry != '.' ) { $entries[] = $fullEntryName; if ( is_dir( $fullEntryName ) ) { } elseif ( is_file( $fullEntryName )) { } } else { // $this->log( $fullEntryName.' [r]' ); } } $CurrentDir->close(); return $entries; } } /*====================================================== REMOVED STUFF =======================================================*/ /* ---evil. removed from main class --> inside $this->configuration , 'generateDids' => false --> inside run() // didGeneration is EVIL. When cached files are a lot it can freeze your server if ( $generated && $this->configuration[ 'generateDids' ]) { $this->didGenerate( $this->vars[ 'sourceFilename' ], $this->vars ); } function didGetPath ( $sourceFilename, $v) { return $this->basePath . DIRECTORY_SEPARATOR . '_did' .DIRECTORY_SEPARATOR . urlencode( $sourceFilename ); } function didGenerate ( $sourceFilename, $v ) { $didPath = $this->didGetPath( $sourceFilename, $v ); $mtime = filemtime( $sourceFilename ); $this->didKill( $sourceFilename, $v ); lll ( 'generating did for "'.$sourceFilename.'" :'.$mtime); lll ( 'didPath: '. $didPath ); error_log( $mtime, 3, $didPath ); } function didKill ( $sourceFilename, $v ) { $didPath = $this->didGetPath( $sourceFilename, $v ); if ( file_exists( $didPath ) ) { lll ( 'killing did for "'.$sourceFilename.'" '); return unlink ( $didPath ); } else { lll ( 'no did existant for "'.$sourceFilename.'" '); } return false; } */ ?>