Created
April 16, 2012 14:56
-
-
Save RWOverdijk/2399269 to your computer and use it in GitHub Desktop.
Get custom data from custom files, nested in folders.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Application\Custom; | |
use Zend\Config\Factory as ConfigFactory; | |
class Locator | |
{ | |
const DEFAULT_CUSTOM = '_default'; | |
const CUSTOM_DIRECTORY = '_custom'; | |
const PARAM_DELIMITER = '.'; | |
const CACHE_DIRECTORY = '_cache'; | |
/** | |
* Singleton | |
* @var type Application\Custom\Locator | |
*/ | |
protected static $instance; | |
/** | |
* @var Zend\Config\Config | |
*/ | |
protected $customConfig; | |
/** | |
* @var array | |
*/ | |
protected $customDirectories = array(); | |
/** | |
* @var array | |
*/ | |
protected $filePathCache = array(); | |
/** | |
* @var string | |
*/ | |
protected $domain; | |
/** | |
* @var string | |
*/ | |
protected $cacheDirectory; | |
/** | |
* Construct the locator with an optional custom domain (ignoring HTTP_HOST) | |
* | |
* @param string $domain | |
*/ | |
public function __construct($domain = null) | |
{ | |
$this->setDomain($domain); | |
} | |
/** | |
* Set the domain to use for resolving custom paths. | |
* | |
* @param string $domain | |
* @return Locator Fluent interface | |
*/ | |
protected function setDomain($domain = null) | |
{ | |
$domain = ($domain !== null && is_string($domain)) ? $domain : $_SERVER['HTTP_HOST']; | |
$protocol = (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://'; | |
$domain = str_replace($protocol, '', $domain); | |
$this->domain = $protocol; | |
if (null !== $domain && is_string($domain)) { | |
$this->domain .= $domain; | |
} else { | |
$this->domain .= $_SERVER['HTTP_HOST']; | |
} | |
return $this; | |
} | |
/** | |
* Get the custom configuration, merged with the default. | |
* | |
* @param string The config file to load | |
* @return \Zend\Config | |
* @throws \InvalidArgumentException | |
*/ | |
public function getCustomConfig($fileName = '_custom.xml') | |
{ | |
if ($this->customConfig !== null) { | |
return $this->customConfig; | |
} | |
$file = $this->find($fileName); | |
if (!$file) { | |
throw new \InvalidArgumentException( | |
'The filename supplied does not link to a valid configuration file, or the file is not readable.' | |
); | |
} | |
$config = ConfigFactory::fromFile($file, true); | |
if (!$this->isDefaultPath($file)) { | |
$defaultPath = PUBLIC_DIRECTORY.'/'.self::CUSTOM_DIRECTORY.'/'.self::DEFAULT_CUSTOM.'/'.$fileName; | |
$configDefault = ConfigFactory::fromFile($defaultPath, true); | |
$config = $configDefault->merge($config); | |
} | |
return $this->customConfig = $config; | |
} | |
/** | |
* Get whether or not a certain path is a default path. | |
* | |
* @param string $path | |
* @return boolean | |
*/ | |
protected function isDefaultPath($path) | |
{ | |
return (bool)strstr(PUBLIC_DIRECTORY.'/'.self::CUSTOM_DIRECTORY.'/'.self::DEFAULT_CUSTOM, $path); | |
} | |
/** | |
* Get an option's value for this instance. | |
* Nested objects can be accessed by separating them with the PARAM_DELIMITER | |
* | |
* @param string $key The key for the option | |
* @return mixed The value found for $key | |
*/ | |
public function get($key) | |
{ | |
$options = $this->getCustomConfig(); | |
if (isset($options[$key])) { | |
return $options[$key]; | |
} | |
if (strpos($key, self::PARAM_DELIMITER) === false) { | |
return null; | |
} | |
$keyParts = explode(self::PARAM_DELIMITER, $key); | |
$tmpVal = $options; | |
foreach ($keyParts as $kp) { | |
if (isset($tmpVal[$kp])) { | |
$tmpVal = $tmpVal[$kp]; | |
continue; | |
} | |
return null; | |
} | |
return $tmpVal; | |
} | |
/** | |
* Returns the first match, and thus most important directory for the current custom url. | |
* | |
* @return string path to directory | |
*/ | |
public function getMasterCustomDir() | |
{ | |
$this->locatePaths(); | |
return $this->customDirectories[0]; | |
} | |
/** | |
* Returns the path to the currently loaded custom's cache dir. | |
* | |
* @return string path to directory | |
*/ | |
public function getCustomCacheDir() | |
{ | |
$this->locatePaths(); | |
return $this->cacheDirectory; | |
} | |
/** | |
* Locate the possible paths for the current (by domain) custom. | |
* | |
* @return array An array of directories that are searchable | |
* @throws \LogicException | |
*/ | |
protected function locatePaths() | |
{ | |
if (!empty($this->customDirectories)) { | |
return $this->customDirectories; | |
} | |
$possibleDirectories = array(); | |
if (($urlData = $this->parseUrl($this->domain))) { | |
if (ENVIRONMENT === 'development' && !empty($urlData['subdomain'])) { | |
$possibleDirectories[] = $urlData['subdomain']; | |
$this->cacheDirectory = PUBLIC_DIRECTORY.'/' . self::CACHE_DIRECTORY . '/' . $urlData['subdomain']; | |
} else { | |
$domainLabel = str_replace('.'.$urlData['tld'], '', $urlData['domain']); | |
$possibleDirectories[] = $domainLabel.$urlData['tld']; | |
$possibleDirectories[] = $domainLabel; | |
$this->cacheDirectory = PUBLIC_DIRECTORY.'/' . self::CACHE_DIRECTORY . '/' . $domainLabel.$urlData['tld']; | |
} | |
} | |
if (is_dir($this->cacheDirectory)) { | |
mkdir($this->cacheDirectory, 0777); | |
} | |
$possibleDirectories[] = self::CACHE_DIRECTORY; | |
$possibleDirectories[] = self::DEFAULT_CUSTOM; | |
foreach ($possibleDirectories as $possibleDirectory) { | |
if (is_dir(($fullDir = PUBLIC_DIRECTORY.'/'.self::CUSTOM_DIRECTORY.'/'.$possibleDirectory))) { | |
$this->customDirectories[] = rtrim($fullDir, '/') . '/'; | |
} else { | |
$this->customDirectories[] = rtrim(PUBLIC_DIRECTORY.'/'.self::CACHE_DIRECTORY.'/'.$possibleDirectory); | |
} | |
} | |
if (empty($this->customDirectories)) { | |
throw new \LogicException( | |
'No custom directories found. At least one (default) directory should have been found.' | |
); | |
} | |
return $this->customDirectories; | |
} | |
/** | |
* Parses the url and returns the full domain (without the subcomain) and the top-level domain. | |
* Also handles cctlds that are not in ISO 3166-1 such as uk, su as well as gtlds. | |
* Expects a RFC 1035 compliant url. | |
* | |
* @param string $uri | |
* @link http://www.ietf.org/rfc/rfc1035.txt | |
*/ | |
protected function parseUrl($url) | |
{ | |
$urlInfo = parse_url($url); | |
$domain = isset($urlInfo['host']) ? $urlInfo['host'] : ''; | |
$regs = array(); | |
if (preg_match('/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.(?P<tld>[a-z\.]{2,6}))$/i', $domain, $regs)) { | |
return array_merge(array( | |
'domain' => $regs['domain'], | |
'tld' => $regs['tld'], | |
'subdomain' => str_replace($urlInfo['scheme'] . '://', '', strstr($url, '.'.$regs['domain'], true)) | |
), $urlInfo); | |
} | |
return false; | |
} | |
/** | |
* Locate the path to $file for this custom. | |
* | |
* @param string $file The path to $file | |
*/ | |
public function find($file) | |
{ | |
if (isset($this->filePathCache[$file])) { | |
return $this->filePathCache[$file]; | |
} | |
$customDirectories = $this->locatePaths(); | |
foreach ($customDirectories as $customDirectory) { | |
if (is_readable(($cFile = $customDirectory.'/'.ltrim($file,'/')))) { | |
$this->filePathCache[$file] = $cFile; | |
return $cFile; | |
} | |
} | |
return false; | |
} | |
/** | |
* Get the instance of Locator, or create a new one when not existant. | |
* | |
* @param string $domain | |
* @return Locator Singleton | |
*/ | |
public static function getInstance($domain = null) | |
{ | |
return null !== static::$instance ? static::$instance : static::$instance = new self($domain); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment