generateIdentifier( $id, $attributes ); if ( file_exists( $filename ) ) { if ( unlink( $filename ) === false ) { throw new ezcCacheStorageException( "Could not store data to file <{$filename}>.", ezcCacheStorageException::LOCATION_NOT_WRITABLE ); } } $dataStr = $this->prepareData( $data ); $dirname = dirname( $filename ); if ( !is_dir( $dirname ) ) { mkdir( $dirname, 0777, true ); } if ( file_put_contents( $filename, $dataStr ) !== strlen( $dataStr ) ) { throw new ezcCacheStorageException( "Could not store data to file <{$filename}>.", ezcCacheStorageException::LOCATION_NOT_AVAILABLE ); } return $id; } /** * Restore data from the cache. * Restores the data associated with the given cache and * returns it. Please see {@link ezcCacheStorage::store()} * for more detailed information of cachable datatypes. * * During access to cached data the caches are automatically * expired. This means, that the ezcCacheStorage object checks * before returning the data if it's still actual. If the cache * has expired, data will be deleted and false is returned. * * You should always provide the attributes you assigned, although * the cache storages must be able to find a cache ID even without * them. BEWARE: Finding cache data only by ID can be much * slower than finding it by ID and attributes. * * @param string $id The cache ID to restore data from. * @param array $attributes Attributed describing the data to restore. * * @return mixed The cached data on success, otherwise false. */ public function restore( $id, $attributes = array() ) { $filename = $this->generateIdentifier( $id, $attributes ); if ( file_exists( $filename ) === false ) { if ( count( $files = $this->search( $id, $attributes ) ) === 1 ) { $filename = $files[0]; } else { return false; } } // No cached data if ( file_exists( $filename ) === false ) { return false; } // Cached data outdated, purge it. if ( $this->calcLifetime( $filename ) > $this->options['ttl'] ) { $this->delete( $id, $attributes ); return false; } return ( $this->fetchData( $filename ) ); } /** * Delete data from the cache. * Purges the cached data for a given ID and or attributes. Using an ID * purges only the cache data for just this ID. * * Additional attributes provided will matched additionally. This can give * you an immense speed improvement against just searching for ID ( see * {@link ezcCacheStorage::restore()} ). * * If you only provide attributes for deletion of cache data, all cache * data matching these attributes will be purged. * * @param string $id The ID of the data to purge. * @param array $attributes Attributes describing the data to purge. */ public function delete( $id = null, $attributes = array() ) { $filename = $this->generateIdentifier( $id, $attributes ); $delFiles = array(); if ( file_exists( $filename ) ) { $delFiles[] = $filename; } else { $delFiles = $this->search( $id, $attributes ); } foreach ( $delFiles as $filename ) { if ( unlink( $filename ) === false ) { throw new ezcCacheStorageException( "Cannot delete cache file <{$filename}>.", ezcCacheStorageException::LOCATION_NOT_WRITEABLE ); } } } /** * Return if cache data is available. * This method determines if cache data described by the given ID and/or * attributes exists. It returns the number of cache data items found. * * @param string $id The ID of the cache data. * @param array $attributes Attributes to describe the data. * @return int The number of cache data items found matching the criteria. */ public function hasData( $id = null, $attributes = array() ) { return ( count( $this->search( $id, $attributes ) ) > 0 ); } /** * Returns the time ( in seconds ) which remains for a cache objes, * before it gets outdated. In case the cache object is already * outdated or does not exists, this method returns 0. * * @param string $id The ID of the cache data. * @param array $attributes Attributes to describe the data. * @access public * @return int The remaining lifetime ( 0 if nonexists or oudated ). */ public function remainingLifetime( $id, $attributes = array() ) { if ( count( $objects = $this->search( $id, $attributes ) ) > 0 ) { $lifetime = $this->calcLifetime( $objects[0] ); return ( ( $remaining = ( $this->options['ttl'] - $lifetime ) ) > 0 ) ? $remaining : 0; } return 0; } /** * Search the storage for data. * * @param string $id The unique identifier provided by you * @param array $attributes Attributes to search for * @access public */ private function search( $id = null, $attributes = array() ) { $glob = strtr( $this->generateIdentifier( $id, $attributes ), array( '-' => '*' ) ) . '*'; $glob = ( isset( $id ) ? '*' : '' ) . $glob; return glob( $glob ); } /** * Checks if a given location is valid. * Checks if the location exists and tries to create it, if not. Also checks * if the location is read-/writeable and throws an exception, if not. */ protected function validateLocation() { if ( file_exists( $this->location ) === false ) { throw new ezcCacheStorageException( "Cannot create storage location <{$this->location}>.", ezcCacheStorageException::LOCATION_NOT_AVAILABLE ); } if ( is_dir( $this->location ) === false ) { throw new ezcCacheStorageException( "Storage location is not a directory: <{$this->location}>.", ezcCacheStorageException::LOCATION_NOT_AVAILABLE ); } if ( is_writeable( $this->location ) === false ) { throw new ezcCacheStorageException( "Storage location is not a directory: <{$this->location}>.", ezcCacheStorageException::LOCATION_NOT_WRITEABLE ); } } /** * Generate the storage internal identifier from ID and attributes. * * Generates the storage internal identifier out of the provided ID and the * attributes. This is the default implementation and can be overloaded if * necessary. * * @param string $id The ID. * @param array(string) $attr The attributes. * @return string The generated identifier */ public function generateIdentifier( $id, $attr = null ) { $filename = (string) $id; $illegalFileNameChars = array( ' ' => '_', '/' => DIRECTORY_SEPARATOR, '\\' => DIRECTORY_SEPARATOR, ); $filename = strtr( $filename, $illegalFileNameChars ); // Chars used for filename concatination $illegalChars = array( '-' => '#', ' ' => '%', ':' => '+', ); if ( is_array( $attr ) && count( $attr ) > 0 ) { ksort( $attr ); foreach ( $attr as $key => $val ) { $attrStr = '-' . strtr( $key, $illegalChars ) . ':' . strtr( $val, $illegalChars ); if ( strlen( $filename . $attrStr ) > 250 ) { // Max filename length break; } $filename .= $attrStr; } } return $this->location . $filename . $this->options['extension']; } /** * Calculates the lifetime remaining for a cache object. * This calculates the time a cached object stays valid and returns it. * * @param string $file The file to calculate the remaining lifetime for. * @access protected * @return int The remaining lifetime in seconds ( 0 if no time remaining ). */ protected function calcLifetime( $file ) { if ( ( file_exists( $file ) !== false ) && ( ( $modTime = filemtime( $file ) ) !== false ) ) { return ( ( $lifeTime = ( time() - $modTime ) ) > 0 ) ? $lifeTime : 0; } } } ?>