/**
  * Create a new version from a database row
  *
  * @param array $record
  */
 public function __construct($record)
 {
     $this->record = $record;
     $record['ID'] = $record['RecordID'];
     $className = $record['ClassName'];
     $this->object = ClassInfo::exists($className) ? new $className($record) : new DataObject($record);
     $this->failover = $this->object;
     parent::__construct();
 }
 public function testExists()
 {
     $this->assertTrue(ClassInfo::exists('SilverStripe\\Core\\Object'));
     $this->assertTrue(ClassInfo::exists('SilverStripe\\Core\\object'));
     $this->assertTrue(ClassInfo::exists('ClassInfoTest'));
     $this->assertTrue(ClassInfo::exists('CLASSINFOTEST'));
     $this->assertTrue(ClassInfo::exists('stdClass'));
     $this->assertTrue(ClassInfo::exists('stdCLASS'));
     $this->assertFalse(ClassInfo::exists('SomeNonExistantClass'));
 }
 /**
  * @skipUpgrade
  */
 public function testFindClassOrInterfaceFromCandidateImports()
 {
     $method = new ReflectionMethod($this->manifest, 'findClassOrInterfaceFromCandidateImports');
     $method->setAccessible(true);
     $this->assertTrue(ClassInfo::exists('silverstripe\\test\\ClassA'));
     $this->assertEquals('PermissionProvider', $method->invokeArgs($this->manifest, ['\\PermissionProvider', 'Test\\Namespace', array('TestOnly', 'Controller')]));
     $this->assertEquals('PermissionProvider', $method->invokeArgs($this->manifest, array('PermissionProvider', 'Test\\NAmespace', array('PermissionProvider'))));
     $this->assertEmpty($method->invokeArgs($this->manifest, array('', 'TextNamespace', array('PermissionProvider'))));
     $this->assertEmpty($method->invokeArgs($this->manifest, array('', '', array())));
     $this->assertEquals('silverstripe\\test\\ClassA', $method->invokeArgs($this->manifest, array('ClassA', 'Test\\Namespace', array('silverstripe\\test\\ClassA', 'PermissionProvider'))));
     $this->assertEquals('ClassA', $method->invokeArgs($this->manifest, array('\\ClassA', 'Test\\Namespace', array('silverstripe\\test'))));
     $this->assertEquals('ClassA', $method->invokeArgs($this->manifest, array('ClassA', 'silverstripe\\test', array('\\ClassA'))));
     $this->assertEquals('ClassA', $method->invokeArgs($this->manifest, array('Alias', 'silverstripe\\test', array('Alias' => '\\ClassA'))));
     $this->assertEquals('silverstripe\\test\\ClassA', $method->invokeArgs($this->manifest, array('ClassA', 'silverstripe\\test', array('silverstripe\\test\\ClassB'))));
 }
 /**
  * Gets name of this class
  *
  * @return string
  */
 public function getClassName()
 {
     $className = $this->getField("ClassName");
     if (!ClassInfo::exists($className)) {
         return static::class;
     }
     return $className;
 }
 /**
  * Return a disabled version of this field.
  *
  * Tries to find a class of the class name of this field suffixed with "_Disabled", failing
  * that, finds a method {@link setDisabled()}.
  *
  * @return FormField
  */
 public function performDisabledTransformation()
 {
     $disabledClassName = $this->class . '_Disabled';
     if (ClassInfo::exists($disabledClassName)) {
         $clone = $this->castedCopy($disabledClassName);
     } else {
         $clone = clone $this;
     }
     $clone->setDisabled(true);
     return $clone;
 }
 /**
  * Matches a URL pattern
  * The pattern can contain a number of segments, separated by / (and an extension indicated by a .)
  *
  * The parts can be either literals, or, if they start with a $ they are interpreted as variables.
  *  - Literals must be provided in order to match
  *  - $Variables are optional
  *  - However, if you put ! at the end of a variable, then it becomes mandatory.
  *
  * For example:
  *  - admin/crm/list will match admin/crm/$Action/$ID/$OtherID, but it won't match admin/crm/$Action!/$ClassName!
  *
  * The pattern can optionally start with an HTTP method and a space.  For example, "POST $Controller/$Action".
  * This is used to define a rule that only matches on a specific HTTP method.
  *
  * @param $pattern
  * @param bool $shiftOnSuccess
  * @return array|bool
  */
 public function match($pattern, $shiftOnSuccess = false)
 {
     // Check if a specific method is required
     if (preg_match('/^([A-Za-z]+) +(.*)$/', $pattern, $matches)) {
         $requiredMethod = $matches[1];
         if ($requiredMethod != $this->httpMethod) {
             return false;
         }
         // If we get this far, we can match the URL pattern as usual.
         $pattern = $matches[2];
     }
     // Special case for the root URL controller
     if (!$pattern) {
         return $this->dirParts == array() ? array('Matched' => true) : false;
     }
     // Check for the '//' marker that represents the "shifting point"
     $doubleSlashPoint = strpos($pattern, '//');
     if ($doubleSlashPoint !== false) {
         $shiftCount = substr_count(substr($pattern, 0, $doubleSlashPoint), '/') + 1;
         $pattern = str_replace('//', '/', $pattern);
         $patternParts = explode('/', $pattern);
     } else {
         $patternParts = explode('/', $pattern);
         $shiftCount = sizeof($patternParts);
     }
     // Filter out any "empty" matching parts - either from an initial / or a trailing /
     $patternParts = array_values(array_filter($patternParts));
     $arguments = array();
     foreach ($patternParts as $i => $part) {
         $part = trim($part);
         // Match a variable
         if (isset($part[0]) && $part[0] == '$') {
             // A variable ending in ! is required
             if (substr($part, -1) == '!') {
                 $varRequired = true;
                 $varName = substr($part, 1, -1);
             } else {
                 $varRequired = false;
                 $varName = substr($part, 1);
             }
             // Fail if a required variable isn't populated
             if ($varRequired && !isset($this->dirParts[$i])) {
                 return false;
             }
             /** @skipUpgrade */
             $key = "Controller";
             $arguments[$varName] = isset($this->dirParts[$i]) ? $this->dirParts[$i] : null;
             if ($part == '$Controller' && (!ClassInfo::exists($arguments[$key]) || !is_subclass_of($arguments[$key], 'SilverStripe\\Control\\Controller'))) {
                 return false;
             }
             // Literal parts with extension
         } else {
             if (isset($this->dirParts[$i]) && $this->dirParts[$i] . '.' . $this->extension == $part) {
                 continue;
                 // Literal parts must always be there
             } else {
                 if (!isset($this->dirParts[$i]) || $this->dirParts[$i] != $part) {
                     return false;
                 }
             }
         }
     }
     if ($shiftOnSuccess) {
         $this->shift($shiftCount);
         // We keep track of pattern parts that we looked at but didn't shift off.
         // This lets us say that we have *parsed* the whole URL even when we haven't *shifted* it all
         $this->unshiftedButParsedParts = sizeof($patternParts) - $shiftCount;
     }
     $this->latestParams = $arguments;
     // Load the arguments that actually have a value into $this->allParams
     // This ensures that previous values aren't overridden with blanks
     foreach ($arguments as $k => $v) {
         if ($v || !isset($this->allParams[$k])) {
             $this->allParams[$k] = $v;
         }
     }
     if ($arguments === array()) {
         $arguments['_matched'] = true;
     }
     return $arguments;
 }
 /**
  * Returns true if a class or interface name exists in the manifest.
  *
  * @param  string $class
  * @return bool
  */
 public function classExists($class)
 {
     Deprecation::notice('4.0', 'Use ClassInfo::exists.');
     return ClassInfo::exists($class);
 }
 /**
  * @return string name of {@see GridFieldDetailForm_ItemRequest} subclass
  */
 public function getItemRequestClass()
 {
     if ($this->itemRequestClass) {
         return $this->itemRequestClass;
     } else {
         if (ClassInfo::exists(get_class($this) . "_ItemRequest")) {
             return get_class($this) . "_ItemRequest";
         } else {
             return __CLASS__ . '_ItemRequest';
         }
     }
 }
 /**
  * Determine if a class is supporting the Versioned extensions (e.g.
  * $table_Versions does exists).
  *
  * @param string $class Class name
  * @return boolean
  */
 public function canBeVersioned($class)
 {
     return ClassInfo::exists($class) && is_subclass_of($class, DataObject::class) && DataObject::getSchema()->classHasTable($class);
 }
 /**
  * Returns false if the prefilterable parts of the rule aren't met, and true if they are
  *
  * @param  $rules array - A hash of rules as allowed in the only or except portion of a config fragment header
  * @return bool - True if the rules are met, false if not. (Note that depending on whether we were passed an
  *                only or an except rule,
  * which values means accept or reject a fragment
  */
 public function matchesPrefilterVariantRules($rules)
 {
     $matches = "undefined";
     // Needs to be truthy, but not true
     foreach ($rules as $k => $v) {
         switch (strtolower($k)) {
             case 'classexists':
                 $matches = $matches && ClassInfo::exists($v);
                 break;
             case 'moduleexists':
                 $matches = $matches && $this->moduleExists($v);
                 break;
             default:
                 // NOP
         }
         if ($matches === false) {
             return $matches;
         }
     }
     return $matches;
 }
 /**
  * Tries to find the database key on another object that is used to store a
  * relationship to this class. If no join field can be found it defaults to 'ParentID'.
  *
  * If the remote field is polymorphic then $polymorphic is set to true, and the return value
  * is in the form 'Relation' instead of 'RelationID', referencing the composite DBField.
  *
  * @param string $class
  * @param string $component Name of the relation on the current object pointing to the
  * remote object.
  * @param string $type the join type - either 'has_many' or 'belongs_to'
  * @param boolean $polymorphic Flag set to true if the remote join field is polymorphic.
  * @return string
  * @throws Exception
  */
 public function getRemoteJoinField($class, $component, $type = 'has_many', &$polymorphic = false)
 {
     // Extract relation from current object
     if ($type === 'has_many') {
         $remoteClass = $this->hasManyComponent($class, $component, false);
     } else {
         $remoteClass = $this->belongsToComponent($class, $component, false);
     }
     if (empty($remoteClass)) {
         throw new Exception("Unknown {$type} component '{$component}' on class '{$class}'");
     }
     if (!ClassInfo::exists(strtok($remoteClass, '.'))) {
         throw new Exception("Class '{$remoteClass}' not found, but used in {$type} component '{$component}' on class '{$class}'");
     }
     // If presented with an explicit field name (using dot notation) then extract field name
     $remoteField = null;
     if (strpos($remoteClass, '.') !== false) {
         list($remoteClass, $remoteField) = explode('.', $remoteClass);
     }
     // Reference remote has_one to check against
     $remoteRelations = Config::inst()->get($remoteClass, 'has_one');
     // Without an explicit field name, attempt to match the first remote field
     // with the same type as the current class
     if (empty($remoteField)) {
         // look for remote has_one joins on this class or any parent classes
         $remoteRelationsMap = array_flip($remoteRelations);
         foreach (array_reverse(ClassInfo::ancestry($class)) as $class) {
             if (array_key_exists($class, $remoteRelationsMap)) {
                 $remoteField = $remoteRelationsMap[$class];
                 break;
             }
         }
     }
     // In case of an indeterminate remote field show an error
     if (empty($remoteField)) {
         $polymorphic = false;
         $message = "No has_one found on class '{$remoteClass}'";
         if ($type == 'has_many') {
             // include a hint for has_many that is missing a has_one
             $message .= ", the has_many relation from '{$class}' to '{$remoteClass}'";
             $message .= " requires a has_one on '{$remoteClass}'";
         }
         throw new Exception($message);
     }
     // If given an explicit field name ensure the related class specifies this
     if (empty($remoteRelations[$remoteField])) {
         throw new Exception("Missing expected has_one named '{$remoteField}'\n\t\t\t\ton class '{$remoteClass}' referenced by {$type} named '{$component}'\n\t\t\t\ton class {$class}");
     }
     // Inspect resulting found relation
     if ($remoteRelations[$remoteField] === DataObject::class) {
         $polymorphic = true;
         return $remoteField;
         // Composite polymorphic field does not include 'ID' suffix
     } else {
         $polymorphic = false;
         return $remoteField . 'ID';
     }
 }
 /**
  * Persists the YAML data in a FixtureFactory,
  * which in turn saves them into the database.
  * Please use the passed in factory to access the fixtures afterwards.
  *
  * @param  FixtureFactory $factory
  */
 public function writeInto(FixtureFactory $factory)
 {
     $parser = new Parser();
     if (isset($this->fixtureString)) {
         $fixtureContent = $parser->parse($this->fixtureString);
     } else {
         if (!file_exists($this->fixtureFile)) {
             return;
         }
         $contents = file_get_contents($this->fixtureFile);
         $fixtureContent = $parser->parse($contents);
         if (!$fixtureContent) {
             return;
         }
     }
     foreach ($fixtureContent as $class => $items) {
         foreach ($items as $identifier => $data) {
             if (ClassInfo::exists($class)) {
                 $factory->createObject($class, $identifier, $data);
             } else {
                 $factory->createRaw($class, $identifier, $data);
             }
         }
     }
 }
 /**
  * Given a partial class name, attempt to determine the best module to assign strings to.
  *
  * @param string $class Either a FQN class name, or a non-qualified class name.
  * @return string Name of module
  */
 protected function findModuleForClass($class)
 {
     if (ClassInfo::exists($class)) {
         return i18n::get_owner_module($class);
     }
     // If we can't find a class, see if it needs to be fully qualified
     if (strpos($class, '\\') !== false) {
         return null;
     }
     // Find FQN that ends with $class
     $classes = preg_grep('/' . preg_quote("\\{$class}", '\\/') . '$/i', ClassLoader::instance()->getManifest()->getClassNames());
     // Find all modules for candidate classes
     $modules = array_unique(array_map(function ($class) {
         return i18n::get_owner_module($class);
     }, $classes));
     if (count($modules) === 1) {
         return reset($modules);
     }
     // Couldn't find it! Exists in none, or multiple modules.
     return null;
 }