<?php class Bridge { static protected $dirBridge; /** * Holds the active whitelist. * Use Bridge::getWhitelist() instead of accessing this parameter directly! */ private static $whitelist = array(); public function __construct(){ throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.'); } /** * Create a new bridge object * @param string $nameBridge Defined bridge name you want use * @return Bridge object dedicated */ static public function create($nameBridge){ if(!preg_match('@^[A-Z][a-zA-Z0-9-]*$@', $nameBridge)) { $message = <<<EOD 'nameBridge' must start with one uppercase character followed or not by alphanumeric or dash characters! EOD; throw new \InvalidArgumentException($message); } $nameBridge = $nameBridge . 'Bridge'; $pathBridge = self::getDir() . $nameBridge . '.php'; if(!file_exists($pathBridge)) { throw new \Exception('The bridge you looking for does not exist. It should be at path ' . $pathBridge); } require_once $pathBridge; if((new ReflectionClass($nameBridge))->isInstantiable()) { return new $nameBridge(); } return false; } static public function setDir($dirBridge){ if(!is_string($dirBridge)) { throw new \InvalidArgumentException('Dir bridge must be a string.'); } if(!file_exists($dirBridge)) { throw new \Exception('Dir bridge does not exist.'); } self::$dirBridge = $dirBridge; } static public function getDir(){ if(is_null(self::$dirBridge)) { throw new \LogicException(__CLASS__ . ' class need to know bridge path !'); } return self::$dirBridge; } /** * Lists the available bridges. * @return array List of the bridges */ static public function listBridges(){ static $listBridge = array(); // Initialized on first call if(empty($listBridge)) { $dirFiles = scandir(self::getDir()); if($dirFiles !== false) { foreach($dirFiles as $fileName) { if(preg_match('@^([^.]+)Bridge\.php$@U', $fileName, $out)) { $listBridge[] = $out[1]; } } } } return $listBridge; } /** * @return bool Returns true if the given bridge is whitelisted. */ static public function isWhitelisted($name){ return in_array(Bridge::sanitizeBridgeName($name), Bridge::getWhitelist()); } /** * On first call reads the whitelist from WHITELIST. Each line in the file * specifies one bridge that will be placed on the whitelist. An empty file * disables all bridges. '*' enables all bridges. * * @return array Returns a list of whitelisted bridges */ public static function getWhitelist() { static $firstCall = true; // Initialized on first call if($firstCall) { // Create initial whitelist or load from disk if (!file_exists(WHITELIST) && !empty(Bridge::$whitelist)) { file_put_contents(WHITELIST, implode("\n", Bridge::$whitelist)); } else { $contents = trim(file_get_contents(WHITELIST)); if($contents === '*') { // Whitelist all bridges Bridge::$whitelist = Bridge::listBridges(); } else { Bridge::$whitelist = array_map('Bridge::sanitizeBridgeName', explode("\n", $contents)); } } } return Bridge::$whitelist; } public static function setWhitelist($default = array()) { Bridge::$whitelist = array_map('Bridge::sanitizeBridgeName', $default); } /** * @return string Returns a sanitized bridge name if the given name has been * found valid, null otherwise. */ private static function sanitizeBridgeName($name) { if(is_string($name)) { // Trim trailing '.php' if exists if(preg_match('/(.+)(?:\.php)/', $name, $matches)) { $name = $matches[1]; } // Trim trailing 'Bridge' if exists if(preg_match('/(.+)(?:Bridge)/i', $name, $matches)) { $name = $matches[1]; } // The name is valid if a corresponding bridge file is found on disk if(in_array(strtolower($name), array_map('strtolower', Bridge::listBridges()))) { $index = array_search(strtolower($name), array_map('strtolower', Bridge::listBridges())); return Bridge::listBridges()[$index]; } Debug::log('Invalid bridge name specified: "' . $name . '"!'); } return null; // Bad parameter } }