public static function get_extra_config_sources($class = null) { if ($class === null) { $class = get_called_class(); } // If this class is unextendable, NOP if (in_array($class, self::$unextendable_classes)) { return null; } // Variable to hold sources in $sources = null; // Get a list of extensions $extensions = Config::inst()->get($class, 'extensions', Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES); if (!$extensions) { return null; } // Build a list of all sources; $sources = array(); foreach ($extensions as $extension) { list($extensionClass, $extensionArgs) = \Object::parse_class_spec($extension); $sources[] = $extensionClass; if (!class_exists($extensionClass)) { throw new InvalidArgumentException("{$class} references nonexistent {$extensionClass} in \$extensions"); } call_user_func(array($extensionClass, 'add_to_class'), $class, $extensionClass, $extensionArgs); foreach (array_reverse(ClassInfo::ancestry($extensionClass)) as $extensionClassParent) { if (ClassInfo::has_method_from($extensionClassParent, 'get_extra_config', $extensionClassParent)) { $extras = $extensionClassParent::get_extra_config($class, $extensionClass, $extensionArgs); if ($extras) { $sources[] = $extras; } } } } return $sources; }
/** * Check that the given action is allowed to be called from a URL. * It will interrogate {@link self::$allowed_actions} to determine this. * * @param string $action * @return bool * @throws Exception */ public function checkAccessAction($action) { $actionOrigCasing = $action; $action = strtolower($action); $isAllowed = false; $isDefined = false; // Get actions for this specific class (without inheritance) $definingClass = $this->definingClassForAction($actionOrigCasing); $allowedActions = $this->allowedActions($definingClass); // check if specific action is set if (isset($allowedActions[$action])) { $isDefined = true; $test = $allowedActions[$action]; if ($test === true || $test === 1 || $test === '1') { // TRUE should always allow access $isAllowed = true; } elseif (substr($test, 0, 2) == '->') { // Determined by custom method with "->" prefix list($method, $arguments) = Object::parse_class_spec(substr($test, 2)); $isAllowed = call_user_func_array(array($this, $method), $arguments); } else { // Value is a permission code to check the current member against $isAllowed = Permission::check($test); } } elseif (is_array($allowedActions) && ($key = array_search($action, $allowedActions, true)) !== false && is_numeric($key)) { // Allow numeric array notation (search for array value as action instead of key) $isDefined = true; $isAllowed = true; } elseif (is_array($allowedActions) && !count($allowedActions)) { // If defined as empty array, deny action $isAllowed = false; } elseif ($allowedActions === null) { // If undefined, allow action based on configuration $isAllowed = !Config::inst()->get('RequestHandler', 'require_allowed_actions'); } // If we don't have a match in allowed_actions, // whitelist the 'index' action as well as undefined actions based on configuration. if (!$isDefined && ($action == 'index' || empty($action))) { $isAllowed = true; } return $isAllowed; }
public function create($class, array $params = array()) { if (strpos($class, '(') === false) { return parent::create($class, $params); } else { list($class, $params) = Object::parse_class_spec($class); $params = $this->injector->convertServiceProperty($params); return parent::create($class, $params); } }
/** * Check that the given action is allowed to be called from a URL. * It will interrogate {@link self::$allowed_actions} to determine this. */ public function checkAccessAction($action) { $actionOrigCasing = $action; $action = strtolower($action); $allowedActions = $this->allowedActions(); if ($allowedActions) { // check for specific action rules first, and fall back to global rules defined by asterisk foreach (array($action, '*') as $actionOrAll) { // check if specific action is set if (isset($allowedActions[$actionOrAll])) { $test = $allowedActions[$actionOrAll]; if ($test === true || $test === 1 || $test === '1') { // Case 1: TRUE should always allow access return true; } elseif (substr($test, 0, 2) == '->') { // Case 2: Determined by custom method with "->" prefix list($method, $arguments) = Object::parse_class_spec(substr($test, 2)); return call_user_func_array(array($this, $method), $arguments); } else { // Case 3: Value is a permission code to check the current member against return Permission::check($test); } } elseif (($key = array_search($actionOrAll, $allowedActions, true)) !== false && is_numeric($key)) { // Case 4: Allow numeric array notation (search for array value as action instead of key) return true; } } } // If we get here an the action is 'index', then it hasn't been specified, which means that // it should be allowed. if ($action == 'index' || empty($action)) { return true; } if ($allowedActions === null || !$this->config()->get('allowed_actions', Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES)) { // If no allowed_actions are provided, then we should only let through actions that aren't handled by // magic methods we test this by calling the unmagic method_exists. if (method_exists($this, $action)) { // Disallow any methods which aren't defined on RequestHandler or subclasses // (e.g. ViewableData->getSecurityID()) $r = new ReflectionClass(get_class($this)); if ($r->hasMethod($actionOrigCasing)) { $m = $r->getMethod($actionOrigCasing); return $m && is_subclass_of($m->getDeclaringClass()->getName(), 'RequestHandler'); } else { throw new Exception("method_exists() true but ReflectionClass can't find method - PHP is b0kred"); } } else { if (!$this->hasMethod($action)) { // Return true so that a template can handle this action return true; } } } return false; }
public function testParseClassSpec() { // Simple case $this->assertEquals(array('SilverStripe\\ORM\\Versioning\\Versioned', array('Stage', 'Live')), Object::parse_class_spec("SilverStripe\\ORM\\Versioning\\Versioned('Stage','Live')")); // String with commas $this->assertEquals(array('SilverStripe\\ORM\\Versioning\\Versioned', array('Stage,Live', 'Stage')), Object::parse_class_spec("SilverStripe\\ORM\\Versioning\\Versioned('Stage,Live','Stage')")); // String with quotes $this->assertEquals(array('SilverStripe\\ORM\\Versioning\\Versioned', array('Stage\'Stage,Live\'Live', 'Live')), Object::parse_class_spec("SilverStripe\\ORM\\Versioning\\Versioned('Stage\\'Stage,Live\\'Live','Live')")); // True, false and null values $this->assertEquals(array('ClassName', array('string', true, array('string', false))), Object::parse_class_spec('ClassName("string", true, array("string", false))')); $this->assertEquals(array('ClassName', array(true, false, null)), Object::parse_class_spec('ClassName(true, false, null)')); // Array $this->assertEquals(array('Enum', array(array('Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted')), Object::parse_class_spec("Enum(array('Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted')")); // Nested array $this->assertEquals(array('Enum', array(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA', 'UnsubmittedB')), 'Unsubmitted')), Object::parse_class_spec("Enum(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')), 'Unsubmitted')")); // 5.4 Shorthand Array $this->assertEquals(array('Enum', array(array('Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted')), Object::parse_class_spec("Enum(['Accepted', 'Pending', 'Declined', 'Unsubmitted'], 'Unsubmitted')")); // 5.4 Nested shorthand array $this->assertEquals(array('Enum', array(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA', 'UnsubmittedB')), 'Unsubmitted')), Object::parse_class_spec("Enum(['Accepted', 'Pending', 'Declined', ['UnsubmittedA','UnsubmittedB']], 'Unsubmitted')")); // Associative array $this->assertEquals(array('Varchar', array(255, array('nullifyEmpty' => false))), Object::parse_class_spec("Varchar(255, array('nullifyEmpty' => false))")); // Nested associative array $this->assertEquals(array('Test', array('string', array('nested' => array('foo' => 'bar')))), Object::parse_class_spec("Test('string', array('nested' => array('foo' => 'bar')))")); // 5.4 shorthand associative array $this->assertEquals(array('Varchar', array(255, array('nullifyEmpty' => false))), Object::parse_class_spec("Varchar(255, ['nullifyEmpty' => false])")); // 5.4 shorthand nested associative array $this->assertEquals(array('Test', array('string', array('nested' => array('foo' => 'bar')))), Object::parse_class_spec("Test('string', ['nested' => ['foo' => 'bar']])")); // Namespaced class $this->assertEquals(array('Test\\MyClass', array()), Object::parse_class_spec('Test\\MyClass')); // Fully qualified namespaced class $this->assertEquals(array('\\Test\\MyClass', array()), Object::parse_class_spec('\\Test\\MyClass')); }
public function testParseClassSpec() { // Simple case $this->assertEquals(array('Versioned', array('Stage', 'Live')), Object::parse_class_spec("Versioned('Stage','Live')")); // String with commas $this->assertEquals(array('Versioned', array('Stage,Live', 'Stage')), Object::parse_class_spec("Versioned('Stage,Live','Stage')")); // String with quotes $this->assertEquals(array('Versioned', array('Stage\'Stage,Live\'Live', 'Live')), Object::parse_class_spec("Versioned('Stage\\'Stage,Live\\'Live','Live')")); // True, false and null values $this->assertEquals(array('ClassName', array('string', true, array('string', false))), Object::parse_class_spec('ClassName("string", true, array("string", false))')); $this->assertEquals(array('ClassName', array(true, false, null)), Object::parse_class_spec('ClassName(true, false, null)')); // Array $this->assertEquals(array('Enum', array(array('Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted')), Object::parse_class_spec("Enum(array('Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted')")); // Nested array $this->assertEquals(array('Enum', array(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA', 'UnsubmittedB')), 'Unsubmitted')), Object::parse_class_spec("Enum(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')), 'Unsubmitted')")); // 5.4 Shorthand Array $this->assertEquals(array('Enum', array(array('Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted')), Object::parse_class_spec("Enum(['Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted']")); // 5.4 Nested shorthand array $this->assertEquals(array('Enum', array(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA', 'UnsubmittedB')), 'Unsubmitted')), Object::parse_class_spec("Enum(['Accepted', 'Pending', 'Declined', ['UnsubmittedA','UnsubmittedB']], 'Unsubmitted')")); // Namespaced class $this->assertEquals(array('Test\\MyClass', array()), Object::parse_class_spec('Test\\MyClass')); // Fully qualified namespaced class $this->assertEquals(array('\\Test\\MyClass', array()), Object::parse_class_spec('\\Test\\MyClass')); }
public function testParseClassSpec() { // Simple case $this->assertEquals(array('Versioned', array('Stage', 'Live')), Object::parse_class_spec("Versioned('Stage','Live')")); // String with commas $this->assertEquals(array('Versioned', array('Stage,Live', 'Stage')), Object::parse_class_spec("Versioned('Stage,Live','Stage')")); // String with quotes $this->assertEquals(array('Versioned', array('Stage\'Stage,Live\'Live', 'Live')), Object::parse_class_spec("Versioned('Stage\\'Stage,Live\\'Live','Live')")); // Array $this->assertEquals(array('Enum', array(array('Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted')), Object::parse_class_spec("Enum(array('Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted')")); // Nested array $this->assertEquals(array('Enum', array(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA', 'UnsubmittedB')), 'Unsubmitted')), Object::parse_class_spec("Enum(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')), 'Unsubmitted')")); }