/** * @covers Cougar\Cache\CacheFactory::getLocalCache */ public function testGetMemoryCache() { $cache = CacheFactory::getMemoryCache(); $this->assertinstanceOf("Cougar\\Cache\\Cache", $cache); $this->assertEquals("memory", $cache->getCacheType()); }
/** * Initializes the REST resource. If properties are provided the resource * will be loaded. Otherwise, a new resource will be created on save(). * * @history * 2013.09.30: * (AT) Initial release * 2014.08.06: * (AT) Turn the execution cache into a proper memory cache * * @version 2014.08.06 * @author (AT) Alberto Trevino, Brigham Young Univ. <*****@*****.**> * * @param iSecurity $security Security context * @param iCache $cache Cache object * @param iRestClient $rest_client Rest client object * @param mixed $object Object or assoc. array of property values * @param string $view Set view on load * @param bool $strict Whether to perform strict property checking (on by default) * @throws \Cougar\Exceptions\Exception */ public function __construct(iSecurity $security, iCache $cache, iRestClient $rest_client, $object = null, $view = null, $strict = true) { # Get a local and a memory cache # TODO: Set through static property(?) $local_cache = CacheFactory::getLocalCache(); $execution_cache = CacheFactory::getMemoryCache(); # Store the object references $this->__security = $security; $this->__cache = $cache; $this->__restClient = $rest_client; # Add the class name to the default cache prefix $this->__cachePrefix .= "." . get_class($this); # Create our own cache keys $class = get_class($this) . ".WsModel"; $cache_key = Annotations::$annotationsCachePrefix . "." . $class; # Call the parent constructor $this->__constructModel(null, $view); # See if the execution cache has the object properties $parsed_annotations = $execution_cache->get($cache_key); if (!$parsed_annotations) { # See if the annotations came from the cache if ($this->__annotations->cached) { $parsed_annotations = $local_cache->get($cache_key); } } # See if we have pre-parsed annotations if ($parsed_annotations === false) { # Go through the class annotations foreach ($this->__annotations->class as $annotation) { switch ($annotation->name) { case "Allow": # Set all the operation to false $this->__allowCreate = false; $this->__allowRead = false; $this->__allowUpdate = false; $this->__allowDelete = false; $this->__allowQuery = false; # See which operations will be allowed foreach (preg_split("/\\s+/u", strtolower($annotation->value)) as $operation) { switch ($operation) { case "create": $this->__allowCreate = true; break; case "read": $this->__allowRead = true; break; case "update": $this->__allowUpdate = true; break; case "delete": $this->__allowDelete = true; break; case "query": case "list": $this->__allowQuery = true; break; } } break; case "BaseUri": $values = preg_split("/\\s+/u", $annotation->value, 2); if (ENVIRONMENT == strtolower($values[0])) { $this->__baseUri = $values[1]; } else { if (ENVIRONMENT == "local" && strtolower($values[0]) == "development" && !$this->__baseUri) { $this->__baseUri = $values[1]; } } break; case "ResourceID": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__resourceIds[] = $property; $this->__readOnly[$property] = true; } else { throw new Exception("Specified Resource ID property " . $property . " does not exist"); } } } break; case "ReadOnly": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (array_key_exists($property, $this->__readOnly)) { $this->__readOnly[$property] = true; } else { throw new Exception("Specified read-only property " . $property . " does not exist"); } } } break; case "Create": if ($annotation->value) { $values = preg_split("/\\s+/u", $annotation->value, 2); switch (count($values)) { case 2: $this->__createCall["method"] = $values[0]; $this->__createCall["uri"] = $values[1]; break; case 1: $this->__createCall["method"] = $values[0]; $this->__createCall["uri"] = ""; break; } } break; case "CreateGetFields": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__createCall["get"][] = $property; } } } break; case "CreatePostFields": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__createCall["post"][] = $property; } } } break; case "CreateBody": switch (strtolower($annotation->value)) { case "xml": $this->__createCall["bodyType"] = "xml"; break; case "json": $this->__createCall["bodyType"] = "json"; break; case "php": $this->__createCall["bodyType"] = "php"; break; default: throw new Exception("Invalid create call " . "body type: " . $annotation->value); } break; case "Read": if ($annotation->value) { $values = preg_split("/\\s+/u", $annotation->value, 2); switch (count($values)) { case 2: $this->__readCall["method"] = $values[0]; $this->__readCall["uri"] = $values[1]; break; case 1: $this->__readCall["method"] = $values[0]; $this->__readCall["uri"] = ""; break; } } break; case "ReadGetFields": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__readCall["get"][] = $property; } } } break; case "ReadPostFields": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__readCall["post"][] = $property; } } } break; case "ReadBody": switch (strtolower($annotation->value)) { case "xml": $this->__readCall["bodyType"] = "xml"; break; case "json": $this->__readCall["bodyType"] = "json"; break; case "php": $this->__readCall["bodyType"] = "php"; break; default: throw new Exception("Invalid read call " . "body type: " . $annotation->value); } break; case "Update": if ($annotation->value) { $values = preg_split("/\\s+/u", $annotation->value, 2); switch (count($values)) { case 2: $this->__updateCall["method"] = $values[0]; $this->__updateCall["uri"] = $values[1]; break; case 1: $this->__updateCall["method"] = $values[0]; $this->__updateCall["uri"] = ""; break; } } break; case "UpdateGetFields": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__updateCall["get"][] = $property; } } } break; case "UpdatePostFields": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__updateCall["post"][] = $property; } } } break; case "UpdateBody": switch (strtolower($annotation->value)) { case "xml": $this->__updateCall["bodyType"] = "xml"; break; case "json": $this->__updateCall["bodyType"] = "json"; break; case "php": $this->__updateCall["bodyType"] = "php"; break; default: throw new Exception("Invalid update call " . "body type: " . $annotation->value); } break; case "Delete": if ($annotation->value) { $values = preg_split("/\\s+/u", $annotation->value, 2); switch (count($values)) { case 2: $this->__deleteCall["method"] = $values[0]; $this->__deleteCall["uri"] = $values[1]; break; case 1: $this->__deleteCall["method"] = $values[0]; $this->__deleteCall["uri"] = ""; break; } } break; case "DeleteGetFields": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__deleteCall["get"][] = $property; } } } break; case "DeletePostFields": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__deleteCall["post"][] = $property; } } } break; case "DeleteBody": switch (strtolower($annotation->value)) { case "xml": $this->__deleteCall["bodyType"] = "xml"; break; case "json": $this->__deleteCall["bodyType"] = "json"; break; case "php": $this->__deleteCall["bodyType"] = "php"; break; default: throw new Exception("Invalid delete call " . "body type: " . $annotation->value); } break; case "Query": if ($annotation->value) { $values = preg_split("/\\s+/u", $annotation->value, 2); switch (count($values)) { case 2: $this->__queryCall["method"] = $values[0]; $this->__queryCall["uri"] = $values[1]; break; case 1: $this->__queryCall["method"] = $values[0]; $this->__queryCall["uri"] = ""; break; } } break; case "QueryGetFields": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__queryCall["get"][] = $property; } } } break; case "QueryPostFields": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__queryCall["post"][] = $property; } } } break; case "QueryBody": switch (strtolower($annotation->value)) { case "xml": $this->__queryCall["bodyType"] = "xml"; break; case "json": $this->__queryCall["bodyType"] = "json"; break; case "php": $this->__queryCall["bodyType"] = "php"; break; default: throw new Exception("Invalid query call " . "body type: " . $annotation->value); } break; case "QueryList": if ($annotation->value) { foreach (preg_split("/\\s+/u", $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__queryProperties[] = $property; } else { throw new Exception("Specified query property " . $property . " does not exist"); } } } break; case "QueryView": if ($annotation->value) { if (array_key_exists($annotation->value, $this->__views)) { $this->__queryView = $annotation->value; } } break; case "NoQuery": # Here for backward compatibility $this->__allowQuery = false; break; case "CachePrefix": if ($annotation->value) { $this->__cachePrefix = $annotation->value; } break; case "CacheTime": if ($annotation->value) { $this->__cacheTime = (int) $annotation->value; } break; case "VoidCacheEntry": if ($annotation->value) { $this->__voidCacheEntries[] = $annotation->value; } break; case "NoCache": $this->__noCache = true; break; } } # Make sure we have a Base URI if (!$this->__baseUri) { throw new Exception("BaseURI must be specified in " . get_class($this)); } # See if we had query properties if (!$this->__queryProperties) { $this->__queryProperties = $this->__properties; } # Store the record properties in the caches $parsed_annotations = array("baseUri" => $this->__baseUri, "resourceIds" => $this->__resourceIds, "createCall" => $this->__createCall, "readCall" => $this->__readCall, "updateCall" => $this->__updateCall, "deleteCall" => $this->__deleteCall, "allowCreate" => $this->__allowCreate, "allowRead" => $this->__allowRead, "allowUpdate" => $this->__allowUpdate, "allowDelete" => $this->__allowDelete, "allowQuery" => $this->__allowQuery, "queryProperties" => $this->__queryProperties, "queryView" => $this->__queryView, "readOnly" => $this->__readOnly, "cachePrefix" => $this->__cachePrefix, "cacheTime" => $this->__cacheTime, "voidCacheEntries" => $this->__voidCacheEntries, "noCache" => $this->__noCache); $execution_cache->set($cache_key, $parsed_annotations); $local_cache->set($cache_key, $parsed_annotations, Annotations::$cacheTime); } else { # Restore the property values $this->__baseUri = $parsed_annotations["baseUri"]; $this->__resourceIds = $parsed_annotations["resourceIds"]; $this->__createCall = $parsed_annotations["createCall"]; $this->__readCall = $parsed_annotations["readCall"]; $this->__updateCall = $parsed_annotations["updateCall"]; $this->__deleteCall = $parsed_annotations["deleteCall"]; $this->__allowCreate = $parsed_annotations["allowCreate"]; $this->__allowRead = $parsed_annotations["allowRead"]; $this->__allowUpdate = $parsed_annotations["allowUpdate"]; $this->__allowDelete = $parsed_annotations["allowDelete"]; $this->__allowQuery = $parsed_annotations["allowQuery"]; $this->__queryProperties = $parsed_annotations["queryProperties"]; $this->__queryView = $parsed_annotations["queryView"]; $this->__readOnly = $parsed_annotations["readOnly"]; $this->__cachePrefix = $parsed_annotations["cachePrefix"]; $this->__cacheTime = $parsed_annotations["cacheTime"]; $this->__voidCacheEntries = $parsed_annotations["voidCacheEntries"]; $this->__noCache = $parsed_annotations["noCache"]; } # See if the object we received was an object or array # TODO: override the __import class for this if (is_array($object) || is_object($object)) { # Separate the object's values into primary key and other values $id_values = array(); $values = array(); foreach ($object as $key => $value) { if (array_key_exists($key, $this->__alias)) { if (in_array($this->__alias[$key], $this->__resourceIds)) { # This is a primary key value $id_values[$key] = $value; } else { # This is another value $values[$key] = $value; } } else { # Store the value as-is $values[$key] = $value; } } # Load the resource id values $this->__enforceReadOnly = false; $this->__import($id_values, $view, $strict); # Get the record $this->getRecord(); $this->__enforceReadOnly = true; # Save the initial values foreach ($this->__properties as $property) { $this->__defaultValues[$property] = $this->{$property}; } # Set the other properties $this->__import($values, $view, $strict); } else { $this->__createMode = true; $this->__enforceReadOnly = false; # Save the initial values foreach ($this->__properties as $property) { $this->__defaultValues[$property] = $this->{$property}; } } }
/** * Initializes the database record. If the object provides values for the * primary key properties, then the record is loaded from the database. If * no, a new record is created. * * @history * 2013.09.30: * (AT) Initial release * 2013.10.24: * (AT) Change authorization() call parameter order * 2014.02.18: * (AT) Add support for the unbound annotation * 2014.04.02: * (AT) Switch from using __defaultValues to __previousValues and * __persistenceValues * 2014.08.06: * (AT) Turn the execution cache into a proper memory cache * * @version 2014.08.06 * @author (AT) Alberto Trevino, Brigham Young Univ. <*****@*****.**> * * @param \Cougar\Security\iSecurity $security * Security context * @param \Cougar\Cache\iCache $cache * Cache object * @param \PDO $pdo * Database connection * @param mixed $object * Object or assoc. array of property values * @param string $view * Set view on load * @param bool $strict * Whether to perform strict property checking (on by default) * @throws \Cougar\Exceptions\Exception */ public function __construct(iSecurity $security, iCache $cache, \PDO $pdo, $object = null, $view = null, $strict = true) { # Get a local cache # TODO: Set through static property(?) $local_cache = CacheFactory::getLocalCache(); $execution_cache = CacheFactory::getMemoryCache(); # Store the object references $this->__security = $security; $this->__cache = $cache; $this->__pdo = $pdo; # Add the class name to the default cache prefix $this->__cachePrefix .= "." . get_class($this); # Create our own cache keys $class = get_class($this) . ".PdoModel"; $cache_key = Annotations::$annotationsCachePrefix . "." . $class; # Call the parent constructor $this->__constructModel(null, $view); # See if the execution cache has the object properties $parsed_annotations = $execution_cache->get($cache_key); if (!$parsed_annotations) { # See if the annotations came from the cache if ($this->__annotations->cached) { $parsed_annotations = $local_cache->get($cache_key); } } # See if we have pre-parsed annotations if ($parsed_annotations === false) { # Go through the class annotations foreach ($this->__annotations->class as $annotation) { switch ($annotation->name) { case "Table": # Only take the first table value if (!$this->__table) { $this->__table = $annotation->value; } break; case "Allow": # Set all the operation to false $this->__allowCreate = false; $this->__allowRead = false; $this->__allowUpdate = false; $this->__allowDelete = false; $this->__allowQuery = false; # See which operations will be allowed foreach (preg_split('/\\s+/u', strtolower($annotation->value)) as $operation) { switch ($operation) { case "create": case "insert": $this->__allowCreate = true; break; case "read": case "select": $this->__allowRead = true; break; case "update": $this->__allowUpdate = true; break; case "delete": $this->__allowDelete = true; break; case "query": case "list": $this->__allowQuery = true; } } break; case "Join": case "JOIN": if ($annotation->value) { # See if the join already has the word JOIN if (stripos($annotation->value, "join") === false) { $this->__joins[] = "JOIN " . $annotation->value; } else { $this->__joins[] = $annotation->value; } } break; case "PrimaryKey": if ($annotation->value) { foreach (preg_split('/\\s+/u', $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__primaryKey[] = $property; } else { throw new Exception("Specified primary key " . "property " . $property . " does not exist"); } } } break; case "ReadOnly": if ($annotation->value) { foreach (preg_split('/\\s+/u', $annotation->value) as $property) { if (array_key_exists($property, $this->__readOnly)) { $this->__readOnly[$property] = true; } else { throw new Exception("Specified read-only property " . $property . " does not exist"); } } } break; case "DeleteFlag": if ($annotation->value) { $tmp_array = preg_split('/\\s+/u', $annotation->value, 2); if (count($tmp_array) != 2) { throw new Exception("You must specify a " . "property name and value with " . "@DeleteFlag annotation"); } if (in_array($tmp_array[0], $this->__properties)) { $this->__deleteProperty = $tmp_array[0]; $this->__deletePropertyValue = $tmp_array[1]; } else { throw new Exception("Delete flag property "); } } break; case "QueryList": if ($annotation->value) { foreach (preg_split('/\\s+/u', $annotation->value) as $property) { if (in_array($property, $this->__properties)) { $this->__queryProperties[] = $property; } else { throw new Exception("Specified query property " . $property . " does not exist"); } } } break; case "QueryView": if ($annotation->value) { if (array_key_exists($annotation->value, $this->__views)) { $this->__queryView = $annotation->value; } } break; case "QueryUnique": $this->__queryUnique = true; break; case "NoQuery": # Here for backward compatibility $this->__allowQuery = false; break; case "CachePrefix": if ($annotation->value) { $this->__cachePrefix = $annotation->value; } break; case "CacheTime": if ($annotation->value) { $this->__cacheTime = (int) $annotation->value; } break; case "VoidCacheEntry": if ($annotation->value) { $this->__voidCacheEntries[] = $annotation->value; } break; case "NoCache": $this->__noCache = true; $this->__noQueryCache = true; break; case "NoQueryCache": $this->__noQueryCache = true; break; } } # Make sure the table name has been defined if (!$this->__table) { throw new Exception("You must specify a table name using the " . "@Table annotation in the class document block"); } # Make sure we have a primary key if (!$this->__primaryKey) { throw new Exception("You must specify the columns that make " . "up the Primary Key using the @PrimaryKey annotation in " . "the class document block"); } # Go through the properties foreach ($this->__annotations->properties as $property_name => $annotations) { # Create the property in the column map $this->__columnMap[$property_name] = $property_name; # Go through the annotations foreach ($annotations as $annotation) { switch ($annotation->name) { case "Column": $this->__columnMap[$property_name] = $annotation->value; break; case "Unbound": unset($this->__columnMap[$property_name]); break; case "ReadOnly": $this->__readOnly[$property_name] = true; } } } # See if we had query properties if (!$this->__queryProperties) { # Declare all properties are queryable $this->__queryProperties = array_keys($this->__columnMap); } # Store the record properties in the caches $parsed_annotations = array("table" => $this->__table, "primaryKey" => $this->__primaryKey, "allowSelect" => $this->__allowRead, "allowInsert" => $this->__allowCreate, "allowUpdate" => $this->__allowUpdate, "allowDelete" => $this->__allowDelete, "joins" => $this->__joins, "deleteProperty" => $this->__deleteProperty, "deletePropertyValue" => $this->__deletePropertyValue, "queryProperties" => $this->__queryProperties, "allowQuery" => $this->__allowQuery, "queryView" => $this->__queryView, "queryUnique" => $this->__queryUnique, "cachePrefix" => $this->__cachePrefix, "cacheTime" => $this->__cacheTime, "voidCacheEntries" => $this->__voidCacheEntries, "noCache" => $this->__noCache, "noQueryCache" => $this->__noQueryCache, "columnMap" => $this->__columnMap, "readOnly" => $this->__readOnly); $execution_cache->set($cache_key, $parsed_annotations); $local_cache->set($cache_key, $parsed_annotations, Annotations::$cacheTime); } else { # Restore the property values $this->__table = $parsed_annotations["table"]; $this->__primaryKey = $parsed_annotations["primaryKey"]; $this->__allowRead = $parsed_annotations["allowSelect"]; $this->__allowCreate = $parsed_annotations["allowInsert"]; $this->__allowUpdate = $parsed_annotations["allowUpdate"]; $this->__allowDelete = $parsed_annotations["allowDelete"]; $this->__joins = $parsed_annotations["joins"]; $this->__deleteProperty = $parsed_annotations["deleteProperty"]; $this->__deletePropertyValue = $parsed_annotations["deletePropertyValue"]; $this->__queryProperties = $parsed_annotations["queryProperties"]; $this->__allowQuery = $parsed_annotations["allowQuery"]; $this->__queryView = $parsed_annotations["queryView"]; $this->__queryUnique = $parsed_annotations["queryUnique"]; $this->__cachePrefix = $parsed_annotations["cachePrefix"]; $this->__cacheTime = $parsed_annotations["cacheTime"]; $this->__voidCacheEntries = $parsed_annotations["voidCacheEntries"]; $this->__noCache = $parsed_annotations["noCache"]; $this->__noQueryCache = $parsed_annotations["noQueryCache"]; $this->__columnMap = $parsed_annotations["columnMap"]; $this->__readOnly = $parsed_annotations["readOnly"]; } # See if the object we received was an object or array if (is_array($object) || is_object($object)) { # Separate the object's values into primary key and other values $pk_values = array(); $values = array(); $has_primary_key_values = false; foreach ($object as $key => $value) { if ($this->__caseInsensitive) { $key = strtolower($key); } # See if this is a value we handle if (array_key_exists($key, $this->__alias)) { # Resolve the alias $key = $this->__alias[$key]; # See if this is a primary key value if (in_array($key, $this->__primaryKey)) { # Add the value to our list of primary key values $pk_values[$key] = $value; # See if the value is not equivalent to default if ($value != $this->{$this->__alias[$key]}) { # This is a primary key value; save the value $has_primary_key_values = true; } } else { # This is another value; store it separately $values[$key] = $value; } } } # See if we have primary key values if ($has_primary_key_values) { # Set the PK properties foreach ($pk_values as $key => $value) { $this->{$key} = $value; } # Get the record; method will also cast values $this->getRecord(); # Call the authorization method; we call it after we get the # record since the authorization may be based on the values $this->authorization($this->__security, $this->__allowCreate, $this->__allowRead, $this->__allowUpdate, $this->__allowDelete, $this->__allowQuery, $this->__columnMap, $this->__readOnly, $this->__visible); # Make sure the identity is authorized to read the record if (!$this->__allowRead) { throw new AccessDeniedException("You do not have access to this record"); } } else { # Set up insert mode $this->__insertMode = true; $this->__enforceReadOnly = false; # Set the persistent values from the previous values $this->__persistentValues = $this->__previousValues; } # Set the other properties via the __import method $this->__import($values); } else { # Set the persistent values from the previous (default) values $this->__persistentValues = $this->__previousValues; # Call the authorization method $this->authorization($this->__security, $this->__allowCreate, $this->__allowRead, $this->__allowUpdate, $this->__allowDelete, $this->__allowQuery, $this->__columnMap, $this->__readOnly, $this->__visible); $this->__insertMode = true; $this->__enforceReadOnly = false; } }
/** * Extracts the annotation for the class and parses them into the * __-prefixed protected properties. * * @history * 2013.09.30: * (AT) Initial release * 2014.02.26: * (AT) Extract annotations with extractFromObjectWithInheritance() * 2014.03.05: * (AT) Don't clobber cached annotations when loading parsed annotations * from cache * (AT) Switch from using __defaultValues to __previousValues * 2014.08.06: * (AT) Turn the execution cache into a proper memory cache * * @version 2014.08.06 * @author (AT) Alberto Trevino, Brigham Young Univ. <*****@*****.**> * * @param mixed $object * Assoc. array or object with initial values * @param string $view * Set the given view once values are loaded * @param bool $strict * Whether to perform strict property checking (on by default) * @throws Exception * @throws BadRequestException */ public function __construct($object = null, $view = null, $strict = true) { # Store the value of the requested view (avoid clobbering later) $requested_view = $view; # Get a local cache and a memory cache # TODO: Set through static property(?) $local_cache = CacheFactory::getLocalCache(); $execution_cache = CacheFactory::getMemoryCache(); # Create our cache keys $class = get_class($this) . ".Model"; $cache_key = Annotations::$annotationsCachePrefix . "." . $class; # See if the execution cache has the object properties $parsed_annotations = $execution_cache->get($cache_key); if (!$parsed_annotations) { # Get the annotations $this->__annotations = Annotations::extractFromObjectWithInheritance($this, array(), true, false); # See if the annotations came from the cache $parsed_annotations = false; if ($this->__annotations->cached) { $parsed_annotations = $local_cache->get($cache_key); } } # See if we have pre-parsed annotations if ($parsed_annotations === false) { # Go through the class annotations $view_list = array("__default__"); foreach ($this->__annotations->class as $annotation) { switch ($annotation->name) { case "CaseInsensitive": $this->__caseInsensitive = true; break; case "Views": # See which views are defined $views = preg_split('/\\s+/u', $annotation->value, null, PREG_SPLIT_NO_EMPTY); # Create the views (if we have any) foreach ($views as $view) { $this->__views[$view] = $this->__views["__default__"]; $view_list[] = $view; } break; } } # Go through the public properties of the object foreach (array_keys($this->__annotations->properties) as $property_name) { # Add the property to the list of properties $this->__properties[] = $property_name; # Set the default property options $this->__type[$property_name] = "string"; $this->__readOnly[$property_name] = false; $this->__null[$property_name] = true; $this->__regex[$property_name] = array(); $this->__alias[$property_name] = $property_name; # See if the properties are case-insensitive if ($this->__caseInsensitive) { # Store the lowercase property name as an alias $this->__alias[strtolower($property_name)] = $property_name; } # Set the view-based values foreach ($view_list as $view) { $this->__views[$view]["optional"][$property_name] = false; $this->__views[$view]["visible"][$property_name] = true; $this->__views[$view]["exportAlias"][$property_name] = $property_name; } # Go through the annotations foreach ($this->__annotations->properties[$property_name] as $annotation) { switch ($annotation->name) { case "Alias": case "Column": $this->__alias[$annotation->value] = $property_name; if ($this->__caseInsensitive) { $this->__alias[strtolower($annotation->value)] = $property_name; } break; case "NotNull": $this->__null[$property_name] = false; break; case "Regex": $this->__regex[$property_name][] = $annotation->value; break; case "Optional": # Set the option in all views foreach ($view_list as $view) { $this->__views[$view]["optional"][$property_name] = true; } break; case "DateTimeFormat": $this->__dateTimeFormat[$property_name] = $annotation->value; break; case "View": # Separate the values $view_values = preg_split('/\\s+/u', $annotation->value); # Extract the view (first value) $view = array_shift($view_values); # Make sure the view exists if (!array_key_exists($view, $this->__views)) { throw new Exception($property_name . " property defines \"" . $view . "\" but the view does not exist."); } # Go through the rest of the options $export_alias_set = false; foreach ($view_values as $index => $value) { switch (strtolower($value)) { case "hidden": $this->__views[$view]["visible"][$property_name] = false; break; case "optional": $this->__views[$view]["optional"][$property_name] = true; break; default: # Add the real value (not lowercase) as # the export alias and as an alias if (!$export_alias_set) { $this->__views[$view]["exportAlias"][$property_name] = $view_values[$index]; $this->__alias[$view_values[$index]] = $property_name; if ($this->__caseInsensitive) { $this->__alias[strtolower($view_values[$index])] = $property_name; } } $export_alias_set = true; break; } } break; case "var": # Separate the variable name from the comment $var_values = preg_split('/\\s+/u', $annotation->value); switch ($var_values[0]) { case "string": case "": # Type is already set to string break; case "int": case "integer": $this->__type[$property_name] = "int"; break; case "float": case "double": $this->__type[$property_name] = "float"; break; case "bool": case "boolean": $this->__type[$property_name] = "bool"; break; case "DateTime": $this->__type[$property_name] = "DateTime"; if (!array_key_exists($property_name, $this->__dateTimeFormat)) { $this->__dateTimeFormat[$property_name] = ""; } break; default: $this->__type[$property_name] = $var_values[0]; } break; } } } # Get the default values foreach ($this->__properties as $property) { $this->__defaultValues[$property] = $this->{$property}; } # Store the record properties in the caches $parsed_annotations = array("annotations" => $this->__annotations, "properties" => $this->__properties, "type" => $this->__type, "readOnly" => $this->__readOnly, "null" => $this->__null, "dateTimeFormat" => $this->__dateTimeFormat, "regex" => $this->__regex, "alias" => $this->__alias, "caseInsensitive" => $this->__caseInsensitive, "view" => $this->__views, "defaultValues" => $this->__defaultValues); $execution_cache->set($cache_key, $parsed_annotations); $local_cache->set($cache_key, $parsed_annotations, Annotations::$cacheTime); } else { # Make sure we don't clobber any previous annotations # (otherwise we may lose the cached setting) if (!$this->__annotations) { $this->__annotations = $parsed_annotations["annotations"]; } # Restore the property values $this->__properties = $parsed_annotations["properties"]; $this->__type = $parsed_annotations["type"]; $this->__readOnly = $parsed_annotations["readOnly"]; $this->__null = $parsed_annotations["null"]; $this->__dateTimeFormat = $parsed_annotations["dateTimeFormat"]; $this->__regex = $parsed_annotations["regex"]; $this->__alias = $parsed_annotations["alias"]; $this->__caseInsensitive = $parsed_annotations["caseInsensitive"]; $this->__views = $parsed_annotations["view"]; $this->__defaultValues = $parsed_annotations["defaultValues"]; } # Set the previous values from the default values $this->__previousValues = $this->__defaultValues; # See if we have an incoming object or array if (is_array($object) || is_object($object)) { # Load the incoming values $this->__import($object, $strict); } else { if (!is_null($object)) { throw new BadRequestException("Casting from object requires an object or array"); } } # Set the desired view if ($requested_view) { $this->__setView($requested_view); } else { # Point the protected properties to the values in the default view $this->__exportAlias =& $this->__views["__default__"]["exportAlias"]; $this->__optional =& $this->__views["__default__"]["optional"]; $this->__visible =& $this->__views["__default__"]["visible"]; } }