Created
February 9, 2010 02:53
-
-
Save sminnee/298870 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| Index: cms/code/CMSMain.php | |
| =================================================================== | |
| --- cms/code/CMSMain.php (revision 98430) | |
| +++ cms/code/CMSMain.php (working copy) | |
| @@ -536,7 +536,7 @@ | |
| // DataObject::fieldExists only checks the current class, not the hierarchy | |
| // This allows the CMS to set the correct sort value | |
| - if($newItem->castingHelperPair('Sort')) { | |
| + if($newItem->castingHelper('Sort')) { | |
| $newItem->Sort = DB::query("SELECT MAX(\"Sort\") FROM \"SiteTree\" WHERE \"ParentID\" = '" . Convert::raw2sql($parentID) . "'")->value() + 1; | |
| } | |
| Index: sapphire/core/ViewableData.php | |
| =================================================================== | |
| --- sapphire/core/ViewableData.php (revision 98430) | |
| +++ sapphire/core/ViewableData.php (working copy) | |
| @@ -74,11 +74,7 @@ | |
| * @return string | |
| */ | |
| public static function castingObjectCreator($fieldSchema) { | |
| - if(strpos($fieldSchema, '(') === false) { | |
| - return "return Object::create('{$fieldSchema}', \$fieldName);"; | |
| - } else { | |
| - return 'return Object::create(' . preg_replace('/^([^(]+)\(/', '\'$1\', $fieldName, ', $fieldSchema) . ';'; | |
| - } | |
| + user_error("Deprecated in a breaking way, use Object::create_from_string()", E_USER_WARNING); | |
| } | |
| /** | |
| @@ -89,21 +85,7 @@ | |
| * @return array | |
| */ | |
| public static function castingObjectCreatorPair($fieldSchema) { | |
| - if(strpos($fieldSchema, '(') === false) { | |
| - return array ( | |
| - 'className' => $fieldSchema, | |
| - 'castingHelper' => self::castingObjectCreator($fieldSchema) | |
| - ); | |
| - } | |
| - | |
| - if(preg_match('/^([^(]+)\(/', $fieldSchema, $parts)) { | |
| - return array ( | |
| - 'className' => $parts[1], | |
| - 'castingHelper' => self::castingObjectCreator($fieldSchema) | |
| - ); | |
| - } | |
| - | |
| - throw new InvalidArgumentException("ViewableData::castingObjectCreatorPair(): bad field schema '$fieldSchema'"); | |
| + user_error("Deprecated in a breaking way, use Object::create_from_string()", E_USER_WARNING); | |
| } | |
| // FIELD GETTERS & SETTERS ----------------------------------------------------------------------------------------- | |
| @@ -251,15 +233,8 @@ | |
| * @return array | |
| */ | |
| public function castingHelperPair($field) { | |
| - if(!isset(self::$casting_cache[$this->class])) { | |
| - if($this->failover) { | |
| - $this->failover->buildCastingCache(self::$casting_cache[$this->class]); | |
| - } | |
| - | |
| - $this->buildCastingCache(self::$casting_cache[$this->class]); | |
| - } | |
| - | |
| - if(isset(self::$casting_cache[$this->class][$field])) return self::$casting_cache[$this->class][$field]; | |
| + user_error("castingHelperPair() Deprecated, use castingHelper() instead", E_USER_NOTICE); | |
| + return $this->castingHelper($field); | |
| } | |
| /** | |
| @@ -270,7 +245,12 @@ | |
| * @return string | |
| */ | |
| public function castingHelper($field) { | |
| - if($pair = $this->castingHelperPair($field)) return $pair['castingHelper']; | |
| + if($this instanceof DataObject && ($fieldSpec = $this->db($field))) { | |
| + return $fieldSpec; | |
| + } | |
| + | |
| + $specs = Object::combined_static(get_class($this), 'casting'); | |
| + if(isset($specs[$field])) return $specs[$field]; | |
| } | |
| /** | |
| @@ -280,7 +260,12 @@ | |
| * @return string | |
| */ | |
| public function castingClass($field) { | |
| - if($pair = $this->castingHelperPair($field)) return $pair['className']; | |
| + $spec = $this->castingHelper($field); | |
| + if(!$spec) return null; | |
| + | |
| + $bPos = strpos($spec,'('); | |
| + if($bPos === false) return $spec; | |
| + else return substr($spec, 0, $bPos-1); | |
| } | |
| /** | |
| @@ -386,10 +371,10 @@ | |
| if(!is_object($value) && ($this->castingClass($fieldName) || $forceReturnedObject)) { | |
| if(!$castConstructor = $this->castingHelper($fieldName)) { | |
| - $castConstructor = self::castingObjectCreator($this->stat('default_cast')); | |
| + $castConstructor = $this->stat('default_cast'); | |
| } | |
| - $valueObject = eval($castConstructor); | |
| + $valueObject = Object::create_from_string($castConstructor, $fieldName); | |
| $valueObject->setValue($value, ($this->hasMethod('getAllFields') ? $this->getAllFields() : null)); | |
| $value = $valueObject; | |
| Index: sapphire/core/model/DataObject.php | |
| =================================================================== | |
| --- sapphire/core/model/DataObject.php (revision 98430) | |
| +++ sapphire/core/model/DataObject.php (working copy) | |
| @@ -1522,6 +1522,8 @@ | |
| } | |
| } | |
| + private static $_cache_db = array(); | |
| + | |
| /** | |
| * Return all of the database fields defined in self::$db and all the parent classes. | |
| * Doesn't include any fields specified by self::$has_one. Use $this->has_one() to get these fields | |
| @@ -1530,26 +1532,23 @@ | |
| * @return array The database fields | |
| */ | |
| public function db($fieldName = null) { | |
| - $classes = ClassInfo::ancestry($this); | |
| - $good = false; | |
| - $items = array(); | |
| + // To do: flush the cache when Object::add_extension() is called. | |
| - foreach($classes as $class) { | |
| - // Wait until after we reach DataObject | |
| - if(!$good) { | |
| - if($class == 'DataObject') { | |
| - $good = true; | |
| + // Cache the combined data | |
| + if(!isset(self::$_cache_db[get_class($this)])) { | |
| + $classes = ClassInfo::ancestry($this); | |
| + $good = false; | |
| + $items = array(); | |
| + | |
| + foreach($classes as $class) { | |
| + // Wait until after we reach DataObject | |
| + if(!$good) { | |
| + if($class == 'DataObject') { | |
| + $good = true; | |
| + } | |
| + continue; | |
| } | |
| - continue; | |
| - } | |
| - if($fieldName) { | |
| - $db = Object::uninherited_static($class, 'db'); | |
| - | |
| - if(isset($db[$fieldName])) { | |
| - return $db[$fieldName]; | |
| - } | |
| - } else { | |
| $newItems = (array) Object::uninherited_static($class, 'db'); | |
| // Validate the data | |
| foreach($newItems as $k => $v) { | |
| @@ -1558,9 +1557,11 @@ | |
| } | |
| $items = isset($items) ? array_merge((array)$items, $newItems) : $newItems; | |
| } | |
| + self::$_cache_db[get_class($this)] = $items; | |
| } | |
| - | |
| - return $items; | |
| + | |
| + if($fieldName) return isset(self::$_cache_db[get_class($this)][$fieldName]) ? self::$_cache_db[get_class($this)][$fieldName] : null; | |
| + else return self::$_cache_db[get_class($this)]; | |
| } | |
| /** | |
| @@ -1957,10 +1958,8 @@ | |
| // Otherwise, we need to determine if this is a complex field | |
| if(self::is_composite_field($this->class, $field)) { | |
| - $helperPair = $this->castingHelperPair($field); | |
| - $constructor = $helperPair['castingHelper']; | |
| - $fieldName = $field; | |
| - $fieldObj = eval($constructor); | |
| + $helper = $this->castingHelper($field); | |
| + $fieldObj = Object::create_from_string($helper, $field); | |
| // write value only if either the field value exists, | |
| // or a valid record has been loaded from the database | |
| @@ -2106,7 +2105,7 @@ | |
| } | |
| $castingHelper = $this->castingHelper($fieldName); | |
| if($castingHelper) { | |
| - $fieldObj = eval($castingHelper); | |
| + $fieldObj = Object::create_from_string($castingHelper, $fieldName); | |
| $fieldObj->setValue($val); | |
| $fieldObj->saveInto($this); | |
| } else { | |
| @@ -2359,9 +2358,8 @@ | |
| return new PrimaryKey($fieldName, $this); | |
| // General casting information for items in $db or $casting | |
| - } else if($helperPair = $this->castingHelperPair($fieldName)) { | |
| - $constructor = $helperPair['castingHelper']; | |
| - $obj = eval($constructor); | |
| + } else if($helper = $this->castingHelper($fieldName)) { | |
| + $obj = Object::create_from_string($helper, $fieldName); | |
| $obj->setValue($this->$fieldName, $this->record, false); | |
| return $obj; | |
| @@ -2395,8 +2393,8 @@ | |
| $component = singleton($rel); | |
| } elseif ($rel = $component->many_many($relation)) { | |
| $component = singleton($rel[1]); | |
| - } elseif($info = $this->castingHelperPair($relation)) { | |
| - $component = singleton($info['className']); | |
| + } elseif($className = $this->castingClass($relation)) { | |
| + $component = $className; | |
| } | |
| } | |
| Index: sapphire/core/Object.php | |
| =================================================================== | |
| --- sapphire/core/Object.php (revision 98430) | |
| +++ sapphire/core/Object.php (working copy) | |
| @@ -96,6 +96,75 @@ | |
| } | |
| } | |
| + private static $_cache_inst_args = array(); | |
| + | |
| + /** | |
| + * Create an object from a string representation. It treats it as a PHP constructor without the | |
| + * 'new' keyword. It also manages to construct the object without the use of eval(). | |
| + * | |
| + * Construction itself is done with Object::create(), so that Object::useCustomClass() calls | |
| + * are respected. | |
| + * | |
| + * `Object::create_from_string("Versioned('Stage','Live')")` will return the result of | |
| + * `Object::create('Versioned', 'Stage', 'Live);` | |
| + * | |
| + * It is designed for simple, clonable objects. The first time this method is called for a given | |
| + * string it is cached, and clones of that object are returned. | |
| + * | |
| + * If you pass the $firstArg argument, this will be prepended to the constructor arguments. It's | |
| + * impossible to pass null as the firstArg argument. | |
| + * | |
| + * `Object::create_from_string("Varchar(50)", "MyField")` will return the result of | |
| + * `Object::create('Vachar', 'MyField', '50');` | |
| + * | |
| + * Arguments are always strings, although this is a quirk of the current implementation rather | |
| + * than something that can be relied upon. | |
| + */ | |
| + static function create_from_string($classSpec, $firstArg = null) { | |
| + if(!isset(self::$_cache_inst_args[$classSpec.$firstArg])) { | |
| + // an $extension value can contain parameters as a string, | |
| + // e.g. "Versioned('Stage','Live')" | |
| + if(strpos($classSpec,'(') === false) { | |
| + if($firstArg === null) self::$_cache_inst_args[$classSpec.$firstArg] = Object::create($classSpec); | |
| + else self::$_cache_inst_args[$classSpec.$firstArg] = Object::create($classSpec, $firstArg); | |
| + | |
| + } else { | |
| + // Break down into class and args | |
| + preg_match('/([^(]+)\((.*)\)/', $classSpec, $matches); | |
| + $class = $matches[1]; | |
| + | |
| + // This is simplistic - allows for args containing commas | |
| + $args = explode(',', $matches[2]); | |
| + foreach($args as $i => $arg) { | |
| + if(!isset($args[$i])) continue; | |
| + $arg = ltrim($arg); | |
| + $lookahead=1; | |
| + while(($arg[0] == "'" && substr_count($arg, "'")==1) | |
| + || ($arg[0] == '"' && substr_count($arg, '"')==1)) { | |
| + $qPos = strpos($args[$i+$lookahead],$args[0]); | |
| + if($qPos === false) { | |
| + $arg = $args[$i] .= ',' . $args[$i+$lookahead]; | |
| + unset($args[$i+$lookahead]); | |
| + $lookahead++; | |
| + } else { | |
| + $arg = $args[$i] .= ','.substr($args[$i+$lookahead],0,$qPos-1); | |
| + $args[$i+$lookahead] = substr($args[$i+$lookahead],$qPos); | |
| + break; | |
| + } | |
| + } | |
| + $args[$i] = preg_replace('/(^["\'])|(["\']$)/','',trim($args[$i])); | |
| + } | |
| + | |
| + if($firstArg !== null) array_unshift($args, $firstArg); | |
| + array_unshift($args, $class); | |
| + | |
| + self::$_cache_inst_args[$classSpec.$firstArg] = call_user_func_array('Object::create', $args); | |
| + } | |
| + } | |
| + | |
| + return clone self::$_cache_inst_args[$classSpec.$firstArg]; | |
| + } | |
| + | |
| /** | |
| * Similar to {@link Object::create()}, except that classes are only overloaded if you set the $strong parameter to | |
| * TRUE when using {@link Object::useCustomClass()} | |
| @@ -538,11 +607,7 @@ | |
| if($extensions = self::uninherited_static($class, 'extensions')) { | |
| foreach($extensions as $extension) { | |
| - // an $extension value can contain parameters as a string, | |
| - // e.g. "Versioned('Stage','Live')" | |
| - if(strpos($extension,'(') === false) $instance = new $extension(); | |
| - else $instance = eval("return new $extension;"); | |
| - | |
| + $instance = self::create_from_string($extension); | |
| $instance->setOwner(null, $class); | |
| $this->extension_instances[$instance->class] = $instance; | |
| } | |
| @@ -555,7 +620,6 @@ | |
| } | |
| } | |
| - | |
| /** | |
| * Attemps to locate and call a method dynamically added to a class at runtime if a default cannot be located | |
| * |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment