PageRenderTime 106ms CodeModel.GetById 61ms app.highlight 20ms RepoModel.GetById 20ms app.codeStats 0ms

/application/libraries/Engine/Api.php

https://github.com/shopaholiccompany/shopaholic
PHP | 645 lines | 340 code | 82 blank | 223 comment | 59 complexity | 991de860ad8ab92c66a898f46252efbf MD5 | raw file
  1<?php
  2/**
  3 * SocialEngine
  4 *
  5 * @category   Engine
  6 * @package    Engine_Api
  7 * @copyright  Copyright 2006-2010 Webligo Developments
  8 * @license    http://www.socialengine.net/license/
  9 * @version    $Id: Api.php 7244 2010-09-01 01:49:53Z john $
 10 * @author     John Boehr <j@webligo.com>
 11 */
 12
 13/**
 14 * @category   Engine
 15 * @package    Engine_Api
 16 * @copyright  Copyright 2006-2010 Webligo Developments
 17 * @license    http://www.socialengine.net/license/
 18 */
 19class Engine_Api
 20{
 21  /**
 22   * The singleton Api object
 23   *
 24   * @var Engine_Api
 25   */
 26  protected static $_instance;
 27
 28  /**
 29   * The current application instance
 30   * 
 31   * @var Engine_Application
 32   */
 33  protected $_application;
 34
 35  /**
 36   * An array of module api objects
 37   * 
 38   * @var array
 39   */
 40  protected $_modules = array();
 41
 42  /**
 43   * Contains the current set module name
 44   * @var string
 45   */
 46  protected $_currentModuleName;
 47  
 48  /**
 49   * @var array assoc map of item type => module
 50   */
 51  protected $_itemTypes;
 52
 53  /**
 54   * Get or create the current api instance
 55   * 
 56   * @return Engine_Api
 57   */
 58  public static function getInstance()
 59  {
 60    if( is_null(self::$_instance) )
 61    {
 62      self::$_instance = new self();
 63    }
 64    
 65    return self::$_instance;
 66  }
 67
 68  /**
 69   * Shorthand for getInstance
 70   *
 71   * @return Engine_Api
 72   */
 73  public static function _()
 74  {
 75    return self::getInstance();
 76  }
 77
 78  /**
 79   * Set or unset the current api instance
 80   * 
 81   * @param Engine_Api $api
 82   * @return Engine_Api
 83   */
 84  public static function setInstance(Engine_Api $api = null) 
 85  {
 86    return self::$_instance = $api;
 87  }
 88
 89  public function getAutoloader()
 90  {
 91    if( null === $this->_autoloader )
 92    {
 93      throw new Exception('Autoloader not set');
 94    }
 95
 96    return $this->_autoloader;
 97  }
 98
 99  public function setAutoloader(Engine_Application_Autoloader $autoloader)
100  {
101    $this->_autoloader = $autoloader;
102    return $this;
103  }
104
105
106
107  // Application
108  
109  /**
110   * Sets the current application instance
111   * 
112   * @param Engine_Application $application
113   * @return Engine_Api
114   */
115  public function setApplication(Engine_Application $application)
116  {
117    $this->_application = $application;
118    return $this;
119  }
120
121  /**
122   * Gets the current application object
123   * 
124   * @return Engine_Application
125   * @throws Engine_Api_Exception If application is not set
126   */
127  public function getApplication()
128  {
129    if( is_null($this->_application) )
130    {
131      throw new Engine_Api_Exception('Application instance not set');
132    }
133    
134    return $this->_application;
135  }
136
137
138
139  // Bootstraps
140  
141  /**
142   * Checks if the specfied module has been bootstrapped
143   * 
144   * @param string $name The module name
145   * @return bool
146   */
147  public function hasModuleBootstrap($name)
148  {
149    return isset($this->_modules[$name]);
150  }
151
152  /**
153   * Sets the local copy of a module bootstrap
154   * 
155   * @param Zend_Application_Module_Bootstrap $bootstrap
156   * @return Engine_Api
157   */
158  public function setModuleBootstrap(Engine_Application_Bootstrap_Abstract $bootstrap)
159  {
160    $name = strtolower($bootstrap->getModuleName());
161    $this->_modules[$name] = $bootstrap;
162    return $this;
163  }
164
165  /**
166   * Gets a module bootstrap
167   * 
168   * @param string $name The module name
169   * @return Zend_Application_Module_Bootstrap|Zend_Application_Bootstrap_Bootstrap
170   * @throws Engine_Api_Exception If module not found
171   */
172  public function getModuleBootstrap($name = null)
173  {
174    if( !$name )
175    {
176      $name = Zend_Controller_Front::getInstance()->getDefaultModule();
177    }
178
179    if( !isset($this->_modules[$name]) )
180    {
181      // Special case, default module can be detected and set
182      if( $name == Zend_Controller_Front::getInstance()->getDefaultModule() )
183      {
184        $this->_modules[$name] = $this->getApplication()->getBootstrap();
185      }
186
187      // Normal modules must be registered manually
188      else
189      {
190        throw new Engine_Api_Exception(sprintf('Module "%s" not found', $name));
191      }
192    }
193
194    return $this->_modules[$name];
195  }
196
197
198
199  // Loading
200  
201  /**
202   * Shorthand for loadModuleApi
203   *
204   * @return Engine_Application_Module_Api
205   * @throws Engine_Api_Exception If given improper arguments or module is missing
206   */
207  public function __call($method, $args)
208  {
209    if( 'get' == substr($method, 0, 3) )
210    {
211      $type = strtolower(substr($method, 3));
212      if( empty($args) )
213      {
214        throw new Engine_Api_Exception("Cannot load resources; no resource specified");
215      }
216      $resource = array_shift($args);
217      $module = array_shift($args);
218      if( $module === null )
219      {
220        if( $this->_currentModuleName === null )
221        {
222          throw new Engine_Api_Exception("Cannot load resources; no module specified");
223        }
224        else
225        {
226          $module = $this->_currentModuleName;
227          $this->_currentModuleName = null;
228        }
229      }
230      
231      return $this->load($module, $type, $resource);
232    }
233
234    // Backwards
235    if( isset($this->_modules[$method]) )
236    {
237      return $this->load($method, 'api', 'core');
238      //return $this->load($method, 'model', 'api');
239    }
240
241    // Boo
242    throw new Engine_Exception("Method '$method' is not supported");
243  }
244
245  /**
246   * Used to shorten some api calls, sets the default module to load resources
247   * from
248   * 
249   * @param string $module
250   * @return Engine_Api
251   */
252  public function setCurrentModule($module)
253  {
254    if( is_string($module) )
255    {
256      $this->_currentModuleName = $module;
257    }
258    else if( is_object($module) && method_exists($object, 'getModuleName') )
259    {
260      $this->_currentModuleName = $object->getModuleName();
261    }
262    else
263    {
264      $this->_currentModuleName = null;
265    }
266
267    return $this;
268  }
269
270  /**
271   * Loads a singleton instance of a module resource
272   *
273   * @param string $module The module name
274   * @param string $type The resource type
275   * @param string $resource The resource name
276   * @return mixed The requested singleton object
277   */
278  public function load($module, $type, $resource)
279  {
280    if( strtolower($type) == 'dbtable' )
281    {
282      $type = 'Model_DbTable';
283    }
284    return Engine_Loader::getInstance()->load(ucfirst($module) . '_' . ucfirst($type) . '_' . ucfirst($resource));
285    //return $this->getModuleBootstrap($module)->getResourceLoader()->load($resource, $type);
286  }
287
288  /**
289   * Loads a singleton instance of a module resource using a full class name
290   *
291   * @param string $class The class name
292   * @return mixed The requested singleton object
293   */
294  public function loadClass($class)
295  {
296    return Engine_Loader::getInstance()->load($class);
297  }
298
299
300
301  // Item handling stuff
302
303  /**
304   * Checks if the item of $type has been registered
305   * 
306   * @param string $type
307   * @return bool
308   */
309  public function hasItemType($type)
310  {
311    $this->_loadItemInfo();
312    return isset($this->_itemTypes[$type]);
313  }
314  
315  /**
316   * Gets an item given a type and identity
317   * 
318   * @param string $type
319   * @param int $identity
320   * @return Core_Model_Item_Abstract
321   */
322  public function getItem($type, $identity)
323  {
324    $this->_loadItemInfo();
325    
326    $api = $this->getItemApi($type);
327
328    $method = 'get'.ucfirst($type);
329    if( method_exists($api, $method) )
330    {
331      return $api->$method($identity);
332    }
333    else if( method_exists($api, 'getItem') )
334    {
335      return $api->getItem($type, $identity);
336    }
337
338    return $this->getItemTable($type)->find($identity)->current();
339  }
340
341  /**
342   * Gets multiple items of a type from an array of ids
343   * 
344   * @param string $type
345   * @param array $identities
346   * @return Engine_Db_Table_Rowset
347   */
348  public function getItemMulti($type, array $identities)
349  {
350    $this->_loadItemInfo();
351    
352    $api = $this->getItemApi($type);
353
354    $method = 'get'.ucfirst($type).'Multi';
355    if( method_exists($api, $method) )
356    {
357      return $api->$method($identities);
358    }
359    else if( method_exists($api, 'getItemMulti') )
360    {
361      return $api->getItemMulti($type, $identities);
362    }
363
364    return $this->getItemTable($type)->find($identities);
365  }
366  
367  /**
368   * Gets an item using a guid array or string
369   * 
370   * @param array|string $guid
371   * @return Core_Model_Item_Abstract
372   * @throws Engine_Api_Exception If given improper arguments
373   */
374  public function getItemByGuid($guid)
375  {
376    $this->_loadItemInfo();
377    
378    if( is_string($guid) )
379    {
380      $guid = explode('_', $guid);
381      if( count($guid) > 2 )
382      {
383        $id = array_pop($guid);
384        $guid = array(join('_', $guid), $id);
385      }
386    }
387    if( !is_array($guid) || count($guid) !== 2 || !is_string($guid[0]) || !is_numeric($guid[1]) )
388    {
389      throw new Engine_Api_Exception(sprintf('Malformed guid passed to getItemByGuid(): %s', join('_', $guid)));
390    }
391    return $this->getItem($guid[0], $guid[1]);
392  }
393
394  /**
395   * Gets the name of the module that an item type belongs to
396   * 
397   * @param string $type The item type
398   * @return string The module name
399   * @throws Engine_Api_Exception If item type isn't registered
400   */
401  public function getItemModule($type)
402  {
403    $this->_loadItemInfo();
404    
405    return $this->getItemInfo($type, 'module');
406  }
407
408  /**
409   * Gets info about an item
410   * 
411   * @param string $type The item type
412   * @param string (OPTIONAL) $key The info key
413   * @return mixed
414   */
415  public function getItemInfo($type, $key = null)
416  {
417    $this->_loadItemInfo();
418    
419    if( empty($this->_itemTypes[$type]) )
420    {
421      throw new Engine_Api_Exception(sprintf("Unknown item type: %s", $type));
422    }
423    
424    if( null === $key )
425    {
426      return $this->_itemTypes[$type];
427    }
428
429    else if( array_key_exists($key, $this->_itemTypes[$type]) )
430    {
431      return $this->_itemTypes[$type][$key];
432    }
433    
434    return null;
435  }
436
437  /**
438   * Gets the class of an item
439   *
440   * @param string $type The item type
441   * @return string The class name
442   */
443  public function getItemClass($type)
444  {
445    $this->_loadItemInfo();
446    
447    // Check api for overriding method
448    $api = $this->getItemApi($type);
449    if( method_exists($api, 'getItemClass') )
450    {
451      return $api->getItemClass($type);
452    }
453
454    // Generate item class manually
455    $module = $this->getItemModule($type);
456    return ucfirst($module) . '_Model_' . self::typeToClassSuffix($type, $module);
457  }
458
459  /**
460   * Gets the class of the dbtable that an item type belongs to
461   *
462   * @param string $type The item type
463   * @return string The table class name
464   */
465  public function getItemTableClass($type)
466  {
467    $this->_loadItemInfo();
468    
469    // Check api for overriding method
470    $api = $this->getItemApi($type);
471    if( method_exists($api, 'getItemTableClass') )
472    {
473      return $api->getItemTableClass($type);
474    }
475
476    // Generate item table class manually
477    $module = $this->getItemInfo($type, 'moduleInflected');
478    $class = $module . '_Model_DbTable_' . self::typeToClassSuffix($type, $module);
479    if( substr($class, -1, 1) === 'y' ) {
480      $class = substr($class, 0, -1) . 'ies';
481    } else if( substr($class, -1, 1) !== 's' ) {
482      $class .= 's';
483    }
484    return $class;
485  }
486
487  /**
488   * Gets a singleton instance of the dbtable an item type belongs to
489   *
490   * @param string $type The item type
491   * @return Engine_Db_Table The table object
492   */
493  public function getItemTable($type)
494  {
495    $this->_loadItemInfo();
496    
497    // Check api for overriding method
498    $api = $this->getItemApi($type);
499    if( method_exists($api, 'getItemTable') )
500    {
501      return $api->getItemTable($type);
502    }
503
504    $class = $this->getItemTableClass($type);
505    return $this->loadClass($class);
506  }
507
508  /**
509   * Gets the item api object that an item type belongs to
510   *
511   * @param string $type The item type
512   * @return Engine_Application_Module_Api
513   */
514  public function getItemApi($type)
515  {
516    $this->_loadItemInfo();
517    
518    $module = $this->getItemInfo($type, 'moduleInflected');
519    return $this->load($module, 'api', 'core');
520  }
521
522  /**
523   * Load item info from manifest
524   */
525  protected function _loadItemInfo()
526  {
527    if( null === $this->_itemTypes )
528    {
529      $manifest = Zend_Registry::get('Engine_Manifest');
530      if( null === $manifest )
531      {
532        throw new Engine_Api_Exception('Manifest data not loaded!');
533      }
534      $this->_itemTypes = array();
535      foreach( $manifest as $module => $config )
536      {
537        if( !isset($config['items']) ) continue;
538        foreach( $config['items'] as $key => $value )
539        {
540          if( is_numeric($key) ) {
541            $this->_itemTypes[$value] = array(
542              'module' => $module,
543              'moduleInflected' => self::inflect($module),
544            );
545          } else {
546            $this->_itemTypes[$key] = $value;
547            $this->_itemTypes[$key]['module'] = $module;
548            $this->_itemTypes[$key]['moduleInflected'] = self::inflect($module);
549          }
550        }
551      }
552    }
553  }
554
555
556
557  // Utility
558
559  static public function inflect($string)
560  {
561    return str_replace(' ', '', ucwords(str_replace(array('.', '-'), ' ' , $string)));
562  }
563
564  static public function deflect($string)
565  {
566    return strtolower(trim(preg_replace('/([a-z0-9])([A-Z])/', '\1-\2', $string), '-. '));
567    //return strtolower(trim(preg_replace('/([a-z0-9])([A-Z])/', '\1-\2', preg_replace('/[^A-Za-z0-9-]/', '', $string)), '-. '));
568  }
569
570  /**
571   * Used to inflect item types to class suffix.
572   * 
573   * @param string $type
574   * @param string $module
575   * @return string
576   */
577  static public function typeToClassSuffix($type, $module)
578  {
579    $parts = explode('_', $type);
580    if( count($parts) > 1 && $parts[0] === strtolower($module) )
581    {
582      array_shift($parts);
583    }
584    $partial = str_replace(' ', '', ucwords(join(' ', $parts)));
585    return $partial;
586  }
587
588  /**
589   * Used to inflect item class to type.
590   * 
591   * @param string $class
592   * @param string $module
593   * @return string
594   * @throws Engine_Api_Exception If given improper arguments
595   */
596  static public function classToType($class, $module)
597  {
598    list($classModule, $resourceType, $resourceName)
599      = explode('_', $class, 3);
600
601    // Throw stuff
602    if( strtolower($classModule) != strtolower($module) )
603    {
604      throw new Engine_Api_Exception('class and module do not match');
605    }
606    else if( $resourceType != 'Model' )
607    {
608      throw new Engine_Api_Exception('resource type must be a model');
609    }
610
611    // Parse camel case
612    preg_match_all('/([A-Z][a-z]+)/', $resourceName, $matches);
613    if( empty($matches[0]) )
614    {
615      throw new Engine_Exception('resource name not useable');
616    }
617    $matches = $matches[0];
618
619    // Append module name if first not equal
620    if( strtolower($matches[0]) != strtolower($module) )
621    {
622      array_unshift($matches, $module);
623    }
624    $type = strtolower(join('_', $matches));
625    return $type;
626  }
627
628  /**
629   * Inflects a type to the class name suffix
630   * @todo Not used?
631   * 
632   * @param string $type
633   * @param string $module
634   * @return string
635   */
636  static public function typeToShort($type, $module)
637  {
638    $parts = explode('_', $type);
639    if( count($parts) > 1 && strtolower($parts[0]) == strtolower($module) )
640    {
641      array_shift($parts);
642    }
643    return strtolower(join('_', $parts));
644  }
645}