Example #1
0
 /**
  * @covers Cougar\Cache\CacheFactory::getApplicationCache
  */
 public function testGetApplicationCache()
 {
     $test_memcache = false;
     if (!defined("APPLICATION_CACHE_CONFIGURATION")) {
         define("APPLICATION_CACHE_CONFIGURATION", "memcache 127.0.0.1:11211");
         $test_memcache = true;
     }
     $cache = CacheFactory::getApplicationCache();
     $this->assertinstanceOf("Cougar\\Cache\\Cache", $cache);
     if ($test_memcache) {
         $this->assertEquals("memcache", $cache->getCacheType());
     }
 }
Example #2
0
 /**
  * Returns the annotations for the class and public methods and properties
  * from the given object. It will also aggregate the annotations in parent
  * classes and optionally from traits and/or interfaces that are directly
  * used by the class.
  *
  * @history
  * 2014.02.26:
  *   (AT)  Initial implementation from deprecated extract() method
  * 2014.03.05:
  *   (AT)  Make sure to set the cached flag on the empty annotations object
  *         so that the child annotation object cached flags may be preserved
  * 2014.03.17:
  *   (AT)  Cache inherited annotations directly to improve performance
  * 2014.03.19:
  *   (AT)  Clone annotations object before storing in execution cache
  * 2014.05.08:
  *   (AT)  Make sure trait and interface filenames are checked for changes
  *
  * @version 2014.05.08
  * @author (AT) Alberto Trevino, Brigham Young Univ. <*****@*****.**>
  *
  * @param mixed $object
  *   Interface name, class name or object to extract from
  * @param array $exclude_class_list
  *   List of classes to exclude from in the object's inheritance tree
  * @param bool $inherit_from_traits
  *   Whether to extract annotations in the trait document block
  * @param bool $inherit_from_interfaces
  *   Whether to extract annotations in the interface document block and its
  *   methods
  * @throws \Cougar\Exceptions\Exception
  * @return \Cougar\Util\ClassAnnotations
  *   ClassAnnotations object with annotations
  */
 public static function extractFromObjectWithInheritance($object, array $exclude_class_list = array(), $inherit_from_traits = true, $inherit_from_interfaces = true)
 {
     // Make sure we have a cache
     if (!self::$cache instanceof iCache) {
         self::$cache = CacheFactory::getLocalCache();
     }
     // Get the name of the object, class or interface
     if (is_object($object)) {
         $object_class_name = get_class($object);
     } else {
         if (is_string($object)) {
             $object_class_name = $object;
         } else {
             throw new Exception("Object must be an object reference or class name");
         }
     }
     // Figure out the cache key
     $cache_key = self::$annotationsCachePrefix . "." . $object_class_name . ".Inherited." . implode(",", $exclude_class_list) . "." . (int) $inherit_from_traits . "." . (int) $inherit_from_interfaces;
     // See if we have an entry in the execution cache
     if (array_key_exists($cache_key, self::$executionCache)) {
         // Return the annotations from the execution cache
         return clone self::$executionCache[$cache_key];
     }
     // Reflect the object
     $r_object = new ReflectionClass($object_class_name);
     // Start the class hierarchy, trait list and filename list
     $class_hierarchy = array();
     $trait_list = array();
     $filename_list = array();
     // Recursively get the parent classes
     $parent = $r_object;
     do {
         // See if class is in the Cougar\Model namespace or the exclude list
         if (substr($parent->name, 0, 12) !== "Cougar\\Model" && !in_array($parent->name, $exclude_class_list)) {
             // Add the class to the hierarchy
             array_unshift($class_hierarchy, $parent->name);
             // Get the class filename
             $filename_list[$parent->name] = $parent->getFileName();
             // See if we are extracting from traits
             if ($inherit_from_traits) {
                 // Go through the traits
                 foreach ($parent->getTraits() as $trait => $r_trait) {
                     // Skip if in Cougar\Model or exclude_class_list
                     if (substr($trait, 0, 12) !== "Cougar\\Model" && !in_array($trait, $exclude_class_list)) {
                         // Add the trait to the class hierarchy
                         array_unshift($class_hierarchy, $trait);
                         // Add the trait to the list of traits
                         $trait_list[] = $trait;
                         // Add the file name to the filename list
                         $filename_list[] = $r_trait->getFileName();
                     }
                 }
             }
             // See if we are extracting from interfaces
             if ($inherit_from_interfaces) {
                 // Go through the interfaces
                 foreach ($parent->getInterfaces() as $interface => $r_interface) {
                     // Skip if in Cougar\Model or exclude_class_list
                     if (substr($interface, 0, 12) !== "Cougar\\Model" && !in_array($interface, $exclude_class_list)) {
                         // Add the interface to the class hierarchy
                         array_unshift($class_hierarchy, $interface);
                         // Add the file name to the filename list
                         $filename_list[] = $r_interface->getFileName();
                     }
                 }
             }
             // Get the parent
             $parent = $parent->getParentClass();
         } else {
             // Don't get the next parent since this class was excluded
             $parent = false;
         }
     } while ($parent !== false);
     // See if the files have been modified
     if (!self::filesHaveChanged(array_unique($filename_list), false)) {
         // See if we have an entry in the local cache
         $annotations = self::$cache->get($cache_key);
         if ($annotations !== false) {
             // Store the annotations in the execution cache
             self::$executionCache[$cache_key] = clone $annotations;
             // Return the cached annotations
             return $annotations;
         }
     }
     // Define an empty set of annotations and consider them cached
     $annotations = new ClassAnnotations();
     $annotations->cached = true;
     // Go through each class, trait and interface
     foreach ($class_hierarchy as $class) {
         // See if we are inheriting from traits and if this is a trait
         if ($inherit_from_traits && in_array($class, $trait_list)) {
             // Get the annotations for the trait
             $trait_annotations = self::extractFromObject($class, false);
             // Trait functions are automatically included in the class; we
             // only need the annotations from the trait definition
             $trait_annotations->properties = array();
             $trait_annotations->methods = array();
             // Merge the annotations
             self::merge($annotations, $trait_annotations);
         } else {
             // Merge the annotations
             self::merge($annotations, self::extractFromObject($class, false));
         }
     }
     // Store the annotations in the cache
     $orig_cache_flag = $annotations->cached;
     $annotations->cached = true;
     self::$executionCache[$cache_key] = clone $annotations;
     self::$cache->set($cache_key, $annotations, self::$cacheTime);
     $annotations->cached = $orig_cache_flag;
     // Return the annotations
     return $annotations;
 }
Example #3
0
 /**
  * Initializes the class map, either by loading it from the cache or
  * rebuilding it from the code.
  *
  * @history
  * 2013.09.30:
  *   (AT)  Initial release
  * 2013.11.13:
  *   (AT)  Actually build the namespace map
  *
  * @version 2013.09.30
  * @author (AT) Alberto Trevino, Brigham Young Univ. <*****@*****.**>
  * @author (JPK) Jillian Koontz, Brigham Young Univ. <*****@*****.**>
  *
  * @param string $path
  *   Path to build from
  * @param bool $rescan
  *   Whether to ignore the cache entries and rescan the path
  * @throws \Cougar\Exceptions\Exception
  */
 protected static function initializeClassMap($path, $rescan = false)
 {
     # Make sure the path exists
     if (!is_dir($path)) {
         throw new Exception("Cannot initialize class map: " . $path . " is not a valid directory");
     }
     # Get a new local cache
     $cache = CacheFactory::getLocalCache();
     # Define the cache key
     $key = self::$cachePrefix . ":" . $path;
     # Get the value from the cache
     $cached_maps = false;
     if (!$rescan) {
         $cached_maps = $cache->get($key);
     }
     # See if we got anything from the cache
     if (!$cached_maps) {
         # Get the list of PHP scripts in the given directory
         $file_list = array();
         $dir_list = array();
         self::findPhpScripts($path, $file_list, $dir_list);
         # Get the classes and namespaces
         $classes = array();
         $namespaces = array();
         self::extractClasses($file_list, $path, $classes, $namespaces);
         # Prepare the cached class map values
         $cached_maps = array("classes" => $classes, "directories" => $dir_list, "namespaces" => $namespaces);
         # Store the class map and directory list in the cache
         $cache->set($key, $cached_maps, self::$cacheTime);
     }
     # Merge the results
     self::$classMap = array_merge(self::$classMap, $cached_maps["classes"]);
     # Add the path to the path list
     self::$paths = array_merge(self::$paths, $cached_maps["directories"]);
     # Merge the namespaces
     foreach ($cached_maps["namespaces"] as $namespace => $namespace_path) {
         if (array_key_exists($namespace, self::$namespaceMap)) {
             if (!in_array($namespace_path, self::$namespaceMap[$namespace])) {
                 self::$namespaceMap[$namespace][] = $namespace_path;
             }
         } else {
             self::$namespaceMap[$namespace] = array($namespace_path);
         }
     }
 }
Example #4
0
 /**
  * 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;
     }
 }
Example #5
0
 /**
  * Loads the given config file
  *
  * @history
  * 2013.09.30:
  *   (AT)  Initial release
  *
  * @version 2013.09.30
  * @author (AT) Alberto Trevino, Brigham Young Univ. <*****@*****.**>
  *
  * @todo: check if the file has been modified since last cached
  *
  * @param string $config_file
  *   Filename of the config file to load; may include full or relative path
  * @throws \Cougar\Exceptions\ConfigurationFileNotFoundException
  */
 public function __construct($config_file)
 {
     # Get a local cache
     $local_cache = CacheFactory::getLocalCache();
     # Create the hash of the calling function
     foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4) as $key => $caller) {
         # Skip ourselves
         if ($key == 0) {
             continue;
         }
         # Make sure this is not a factory method
         if (array_key_exists("class", $caller)) {
             if (strpos($caller["class"], "Factory") !== false) {
                 continue;
             }
         }
         # We found the call we need
         break;
     }
     # Create the cache key for this call
     if (!array_key_exists("file", $caller)) {
         $caller["file"] = "(Unknown)";
     }
     $call_cache_key = self::$cachePrefix . ".Caller." . md5($caller["file"] . ":" . $caller["function"]) . "." . $config_file;
     # See if we have the filename in the cache
     $file = $local_cache->get($call_cache_key);
     if ($file === false) {
         # See if config file name has an absolute path
         $filename = "";
         if (substr($config_file, 0, 1) !== "/") {
             # Find the file
             foreach (self::$subdirList as $dir) {
                 $filename = stream_resolve_include_path($dir . DIRECTORY_SEPARATOR . $config_file);
                 if ($filename) {
                     break;
                 }
             }
         } else {
             # See if the file exists
             if (file_exists($config_file)) {
                 $filename = $config_file;
             }
         }
         # Make sure the file exists
         if (!$filename) {
             throw new ConfigurationFileNotFoundException("File does not exist: " . $config_file);
         }
         # Create the array with the file information
         $file["filename"] = $filename;
         $file["time"] = time();
         # Store the filename in the cache
         $local_cache->set($call_cache_key, $file);
     }
     # See if the contents of the file are in the cache
     $file_cache_key = self::$cachePrefix . ".FileContents." . $file["filename"];
     $values = $local_cache->get($file_cache_key);
     # See if we need to reload the file
     $reload = false;
     if ($values === false) {
         $reload = true;
     } else {
         if (filemtime($file["filename"] > $file["time"])) {
             $reload = true;
         }
     }
     if ($reload) {
         # TODO: Block access to system files
         # Get the file, line by line
         $lines = file($file["filename"], FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES);
         # Go through each line
         $values = array();
         foreach ($lines as $line) {
             # See if we have a hash
             $hash_pos = strpos($line, "#");
             if ($hash_pos !== false) {
                 # Remove the comment
                 $line = substr($line, 0, $hash_pos);
             }
             # Trim the line
             $line = trim($line);
             # See if we have any contents
             if ($line) {
                 # Split on the equal sign
                 $split_line = explode("=", $line, 2);
                 # See how many values we have
                 if (count($split_line) == 2) {
                     # Save the name and the value
                     $values[trim($split_line[0])] = trim($split_line[1]);
                 } else {
                     # Save the name only
                     $values[trim($split_line[0])] = "";
                 }
             }
         }
         # Store the entry in the cache
         $local_cache->set($file_cache_key, $values);
     }
     # Save the values in the protected property
     $this->values = $values;
 }
Example #6
0
 /**
  * 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};
         }
     }
 }
Example #7
0
 /**
  * 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"];
     }
 }
Example #8
0
 /**
  * Stores the Security object and initializes the REST request
  *
  * @history
  * 2013.09.30:
  *   (AT)  Initial release
  *
  * @version 2013.09.30
  * @author (AT) Alberto Trevino, Brigham Young Univ. <*****@*****.**>
  * 
  * @todo Make sure the code works in CGI and Windows environments
  * 
  * @param iSecurity $security Reference to Security context
  */
 public function __construct(iSecurity $security)
 {
     # Call the parent constructor
     parent::__construct();
     # Store the security object
     $this->security = $security;
     # Create a new local cache
     $this->localCache = CacheFactory::getLocalCache();
 }