Skip to content

Instantly share code, notes, and snippets.

@RWOverdijk
Created April 16, 2012 14:56
Show Gist options
  • Save RWOverdijk/2399269 to your computer and use it in GitHub Desktop.
Save RWOverdijk/2399269 to your computer and use it in GitHub Desktop.
Get custom data from custom files, nested in folders.
<?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