<?php

/**
 * Concrete class for APC caching strategy.
 *
 * @instantiator new
 * @author       Time.ly Network, Inc.
 * @since        2.0
 * @package      Ai1EC
 * @subpackage   Ai1EC.Cache.Strategy
 */
class Ai1ec_Cache_Strategy_Apc extends Ai1ec_Cache_Strategy {

	/**
	 * is_available method
	 *
	 * Checks if APC is available for use.
	 * Following pre-requisites are checked: APC functions availability,
	 * APC is enabled via configuration and PHP is not running in CGI.
	 *
	 * @return bool Availability
	 */
	static public function is_available() {
		return function_exists( 'apc_store' ) &&
		       function_exists( 'apc_fetch' ) &&
		       ini_get( 'apc.enabled' ) &&
			   ( false === strpos( php_sapi_name(), 'cgi' ) );
	}

	/**
	 *
	 * @see Ai1ec_Get_Data_From_Cache::get_data()
	 *
	 */
	public function get_data( $dist_key ) {
		$key  = $this->_key( $dist_key );
		$data = apc_fetch( $key );
		if ( false === $data ) {
			throw new Ai1ec_Cache_Not_Set_Exception( "$dist_key not set" );
		}
		return $data;
	}

	/**
	 *
	 * @see Ai1ec_Write_Data_To_Cache::write_data()
	 *
	 */
	public function write_data( $dist_key, $value ) {
		$key          = $this->_key( $dist_key );
		$store_method = 'apc_add';
		if ( false !== ( $existing = apc_fetch( $key ) ) ) {
			if ( $value === $existing ) {

				return true;
			}
			$store_method = 'apc_store';
		} elseif ( false === function_exists( $store_method ) ) {

			$store_method = 'apc_store';
		}
		if ( false === $store_method( $key, $value ) ) {
			try {
				if ( $value !== $this->get_data( $key ) ) {
					throw new Ai1ec_Cache_Not_Set_Exception( 'Data mis-match' );
				}
			} catch ( Ai1ec_Cache_Not_Set_Exception $excpt ) {

				throw new Ai1ec_Cache_Not_Set_Exception(
					'Failed to write ' . $dist_key . ' to APC cache'
				);
			}
		}
		return true;
	}

	/**
	 * (non-PHPdoc)
	 * @see Ai1ec_Write_Data_To_Cache::delete_data()
	 */
	public function delete_data( $key ) {
		if ( false === apc_delete( $this->_key( $key ) ) ) {
			return false;
		}
		return true;
	}

	/**
	 *
	 * @see Ai1ec_Write_Data_To_Cache::delete_matching()
	 */
	public function delete_matching( $pattern ) {
		// not implemented - concider flushing APC cache
		return 0;
	}

	/**
	 * _key method
	 *
	 * Make sure we are on the safe side - in case of multi-instances
	 * environment some prefix is required.
	 *
	 * @param string $key Key to be used against APC cache
	 *
	 * @return string Key with prefix prepended
	 */
	protected function _key( $key ) {
		static $prefix = null;
		if ( NULL === $prefix ) {
			$prefix = substr( md5( ai1ec_get_site_url() ), 0, 8 );
		}
		if ( 0 !== strncmp( $key, $prefix, 8 ) ) {
			$key = $prefix . $key;
		}
		return $key;
	}

}
