diff options
author | matt <matt@59fd770c-687e-43c8-a1e3-f5a4ff64c105> | 2008-12-08 18:30:03 +0300 |
---|---|---|
committer | matt <matt@59fd770c-687e-43c8-a1e3-f5a4ff64c105> | 2008-12-08 18:30:03 +0300 |
commit | 663a05bcc2c9dec65b55f66d8428b925421b286f (patch) | |
tree | 482a13df67ecf66d41ff92adeb403822d7b2b2f6 | |
parent | a7593bab4999ca4e2817056080a6001b70b20eda (diff) |
- refactoring API/*
-rw-r--r-- | core/API/DataTableGenericFilter.php | 131 | ||||
-rw-r--r-- | core/API/DocumentationGenerator.php | 21 | ||||
-rw-r--r-- | core/API/Proxy.php | 206 | ||||
-rw-r--r-- | core/API/Request.php | 51 | ||||
-rw-r--r-- | core/API/ResponseBuilder.php | 138 | ||||
-rw-r--r-- | core/ViewDataTable.php | 6 | ||||
-rw-r--r-- | plugins/API/Controller.php | 22 |
7 files changed, 272 insertions, 303 deletions
diff --git a/core/API/DataTableGenericFilter.php b/core/API/DataTableGenericFilter.php new file mode 100644 index 0000000000..baed81e3d4 --- /dev/null +++ b/core/API/DataTableGenericFilter.php @@ -0,0 +1,131 @@ +<?php + +class Piwik_API_DataTableGenericFilter +{ + function __construct( $datatable, $request ) + { + $this->table = $datatable; + $this->request = $request; + } + + public function filter() + { + $this->applyGenericFilters($this->table); + } + + /** + * Returns an array containing the information of the generic Piwik_DataTable_Filter + * to be applied automatically to the data resulting from the API calls. + * + * Order to apply the filters: + * 1 - Filter that remove filtered rows + * 2 - Filter that sort the remaining rows + * 3 - Filter that keep only a subset of the results + * 4 - Presentation filters + * + * @return array See the code for spec + */ + public static function getGenericFiltersInformation() + { + $genericFilters = array( + 'Pattern' => array( + 'filter_column' => array('string'), + 'filter_pattern' => array('string'), + ), + 'PatternRecursive' => array( + 'filter_column_recursive' => array('string'), + 'filter_pattern_recursive' => array('string'), + ), + 'ExactMatch' => array( + 'filter_exact_column' => array('string'), + 'filter_exact_pattern' => array('array'), + ), + 'ExcludeLowPopulation' => array( + 'filter_excludelowpop' => array('string'), + 'filter_excludelowpop_value'=> array('float'), + ), + 'AddColumnsWhenShowAllColumns' => array( + 'filter_add_columns_when_show_all_columns' => array('integer') + ), + 'Sort' => array( + 'filter_sort_column' => array('string', 'nb_visits'), + 'filter_sort_order' => array('string', Zend_Registry::get('config')->General->dataTable_default_sort_order), + ), + 'Limit' => array( + 'filter_offset' => array('integer', '0'), + 'filter_limit' => array('integer', Zend_Registry::get('config')->General->dataTable_default_limit), + ), + 'SafeDecodeLabel' => array(), + ); + + return $genericFilters; + } + + /** + * Apply generic filters to the DataTable object resulting from the API Call. + * Disable this feature by setting the parameter disable_generic_filters to 1 in the API call request. + * + * @param Piwik_DataTable + * @return void + */ + protected function applyGenericFilters($datatable) + { + if($datatable instanceof Piwik_DataTable_Array ) + { + $tables = $dataTable->getArray(); + foreach($tables as $table) + { + $this->applyDataTableGenericFilters($table); + } + return; + } + + $genericFilters = self::getGenericFiltersInformation(); + + foreach($genericFilters as $filterName => $parameters) + { + $filterParameters = array(); + $exceptionRaised = false; + + foreach($parameters as $name => $info) + { + // parameter type to cast to + $type = $info[0]; + + // default value if specified, when the parameter doesn't have a value + $defaultValue = null; + if(isset($info[1])) + { + $defaultValue = $info[1]; + } + + try { + $value = Piwik_Common::getRequestVar($name, $defaultValue, $type, $this->request); + settype($value, $type); + $filterParameters[] = $value; + } + catch(Exception $e) + { + $exceptionRaised = true; + break; + } + } + + if(!$exceptionRaised) + { + // a generic filter class name must follow this pattern + $class = "Piwik_DataTable_Filter_".$filterName; + if($filterName == 'Limit') + { + $datatable->setRowsCountBeforeLimitFilter(); + } + // build the set of parameters for the filter + $filterParameters = array_merge(array($datatable), $filterParameters); + // use Reflection to create a new instance of the filter, given parameters $filterParameters + $reflectionObj = new ReflectionClass($class); + $filter = $reflectionObj->newInstanceArgs($filterParameters); + } + } + } +} + diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php index ad6018ab51..fb64d1a117 100644 --- a/core/API/DocumentationGenerator.php +++ b/core/API/DocumentationGenerator.php @@ -2,6 +2,25 @@ class Piwik_API_DocumentationGenerator { + protected $countPluginsLoaded = 0; + + /** + * trigger loading all plugins with an API.php file in the Proxy + */ + public function __construct() + { + $plugins = Piwik_PluginsManager::getInstance()->getLoadedPluginsName(); + foreach( $plugins as $plugin ) + { + $plugin = Piwik::unprefixClass($plugin); + try { + Piwik_API_Proxy::getInstance()->registerClass("Piwik_".$plugin."_API"); + } + catch(Exception $e){ + } + } + } + /** * Returns a HTML page containing help for all the successfully loaded APIs. * For each module it will return a mini help with the method names, parameters to give, @@ -69,7 +88,6 @@ class Piwik_API_DocumentationGenerator return $str; } - /** * Returns a string containing links to examples on how to call a given method on a given API * It will export links to XML, CSV, HTML, JSON, PHP, etc. @@ -159,4 +177,5 @@ class Piwik_API_DocumentationGenerator $sParameters = implode(", ", $asParameters); return "($sParameters)"; } + } diff --git a/core/API/Proxy.php b/core/API/Proxy.php index d42a69e218..d9f3e91008 100644 --- a/core/API/Proxy.php +++ b/core/API/Proxy.php @@ -11,19 +11,17 @@ /** - * The API Proxy receives all the API calls requests and forwards them to the given module. - * - * The class checks that a call to the API has the correct number of parameters. - * The proxy is a singleton that has the knowledge of every method available, their parameters and default values. + * Proxy is a singleton that has the knowledge of every method available, their parameters + * and default values. + * Proxy receives all the API calls requests via call() and forwards them to the right + * object, with the parameters in the right order. * - * It can also log the performances of the API calls (time spent, parameter values, etc.) + * It will also log the performance of API calls (time spent, parameter values, etc.) if logger available * * @package Piwik_API */ class Piwik_API_Proxy { - static $classCalled = null; - // array of already registered plugins names protected $alreadyRegistered = array(); @@ -50,6 +48,12 @@ class Piwik_API_Proxy return self::$instance; } + /** + * Returns array containing reflection meta data for all the loaded classes + * eg. number of parameters, method names, etc. + * + * @return array + */ public function getMetadata() { return $this->metadataArray; @@ -65,74 +69,55 @@ class Piwik_API_Proxy * * The method will introspect the methods, their parameters, etc. * - * @param string ModuleName eg. "UserSettings" + * @param string ModuleName eg. "Piwik_UserSettings_API" */ - public function registerClass( $moduleName ) + public function registerClass( $className ) { - if(isset($this->alreadyRegistered[$moduleName])) + if(isset($this->alreadyRegistered[$className])) { return; } - - $this->includeApiFile($moduleName); - $class = $this->getClassNameFromModule($moduleName); - $this->checkClassIsSingleton($class); + $this->includeApiFile( $className ); + $this->checkClassIsSingleton($className); - $rClass = new ReflectionClass($class); + $rClass = new ReflectionClass($className); foreach($rClass->getMethods() as $method) { - $this->loadMethodMetadata($class, $method); + $this->loadMethodMetadata($className, $method); } - $this->alreadyRegistered[$moduleName] = true; + $this->alreadyRegistered[$className] = true; } - - /** - * Checks that the method exists in the class - * - * @param string The class name - * @param string The method name - * @throws exception If the method is not found - */ - public function checkMethodExists($className, $methodName) - { - if(!$this->isMethodAvailable($className, $methodName)) - { - throw new Exception("The method '$methodName' does not exist or is not available in the module '".$className."'."); - } - } - /** - * Magic method used to set a flag telling the module named currently being called - * + * Returns number of classes already loaded + * @return int */ - public function __get($name) + public function getCountRegisteredClasses() { - self::$classCalled = $name; - return $this; - } + return count($this->alreadyRegistered); + } /** - * Method always called when making an API request. - * It checks several things before actually calling the real method on the given module. + * Will execute $className->$methodName($parametersValues) + * If any error is detected (wrong number of parameters, method not found, class not found, etc.) + * it will throw an exception * * It also logs the API calls, with the parameters values, the returned value, the performance, etc. * You can enable logging in config/global.ini.php (log_api_call) * + * @param string The class name (eg. Piwik_Referers_API) * @param string The method name - * @param array The parameters + * @param array The parameters pairs (name=>value) * * @throws Piwik_Access_NoAccessException */ - public function __call($methodName, $parameterValues ) + public function call($className, $methodName, $parametersRequest ) { $returnedValue = null; try { - $this->registerClass(self::$classCalled); - - $className = $this->getClassNameFromModule(self::$classCalled); + $this->registerClass($className); // instanciate the object $object = call_user_func(array($className, "getInstance")); @@ -140,22 +125,27 @@ class Piwik_API_Proxy // check method exists $this->checkMethodExists($className, $methodName); - // all parameters to the function call must be non null - $this->checkParametersAreNotNull($className, $methodName, $parameterValues); + // get the list of parameters required by the method + $parameterNamesDefaultValues = $this->getParametersList($className, $methodName); + // load parameters in the right order, etc. + $finalParameters = $this->getRequestParametersArray( $parameterNamesDefaultValues, $parametersRequest ); + + // all parameters to the function call must be non null + $this->checkParametersAreNotNull($className, $methodName, $finalParameters); + // start the timer $timer = new Piwik_Timer; // call the method - $returnedValue = call_user_func_array(array($object, $methodName), $parameterValues); + $returnedValue = call_user_func_array(array($object, $methodName), $finalParameters); // log the API Call - $parameterNamesDefaultValues = $this->getParametersList($className, $methodName); Zend_Registry::get('logger_api_call')->log( - self::$classCalled, + $className, $methodName, $parameterNamesDefaultValues, - $parameterValues, + $finalParameters, $timer->getTimeMs(), $returnedValue ); @@ -164,24 +154,10 @@ class Piwik_API_Proxy throw $e; } - self::$classCalled = null; - return $returnedValue; } /** - * Returns the 'moduleName' part of 'Piwik_moduleName_API' classname - * - * @param string moduleName - * @return string className - */ - public function getModuleNameFromClassName( $className ) - { - $start = strpos($className, '_') + 1; - return substr($className, $start , strrpos($className, '_') - $start); - } - - /** * Returns the parameters names and default values for the method $name * of the class $class * @@ -198,9 +174,58 @@ class Piwik_API_Proxy return $this->metadataArray[$class][$name]['parameters']; } + /** + * Returns the 'moduleName' part of 'Piwik_moduleName_API' classname + * @param string "Piwik_Referers_API" + * @return string "Referers" + */ + public function getModuleNameFromClassName( $className ) + { + return str_replace(array('Piwik_', '_API'), '', $className); + } + + /** + * Returns an array containing the values of the parameters to pass to the method to call + * + * @param array array of (parameter name, default value) + * @return array values to pass to the function call + * @throws exception If there is a parameter missing from the required function parameters + */ + private function getRequestParametersArray( $requiredParameters, $parametersRequest ) + { + $finalParameters = array(); + foreach($requiredParameters as $name => $defaultValue) + { + try{ + if($defaultValue === Piwik_API_Proxy::NO_DEFAULT_VALUE) + { + try { + $requestValue = Piwik_Common::getRequestVar($name, null, null, $parametersRequest); + } catch(Exception $e) { + $requestValue = null; + } + } + else + { + $requestValue = Piwik_Common::getRequestVar($name, $defaultValue, null, $parametersRequest); + } + } catch(Exception $e) { + throw new Exception("The required variable '$name' is not correct or has not been found in the API Request. Add the parameter '&$name=' (with a value) in the URL."); + } + $finalParameters[] = $requestValue; + } + return $finalParameters; + } + + /** + * Includes the class Piwik_UserSettings_API by looking up plugins/UserSettings/API.php + * + * @param string api class name eg. "Piwik_UserSettings_API" + */ private function includeApiFile($fileName) { - $potentialPaths = array( "plugins/". $fileName ."/API.php", ); + $module = self::getModuleNameFromClassName($fileName); + $potentialPaths = array( "plugins/". $module ."/API.php", ); $found = false; foreach($potentialPaths as $path) @@ -215,21 +240,15 @@ class Piwik_API_Proxy if(!$found) { - throw new Exception("API module $fileName not found."); + throw new Exception("API module $module not found."); } } - + private function loadMethodMetadata($class, $method) { - // use this trick to read the static attribute of the class - // $class::$methodsNotToPublish doesn't work - $variablesClass = get_class_vars($class); - $variablesClass['methodsNotToPublish'][] = 'getInstance'; - if($method->isPublic() && !$method->isConstructor() - && !in_array($method->getName(), $variablesClass['methodsNotToPublish'] ) - ) + && $method->getName() != 'getInstance' ) { $name = $method->getName(); $parameters = $method->getParameters(); @@ -239,7 +258,7 @@ class Piwik_API_Proxy { $nameVariable = $parameter->getName(); - $defaultValue = Piwik_API_Proxy::NO_DEFAULT_VALUE; + $defaultValue = self::NO_DEFAULT_VALUE; if($parameter->isDefaultValueAvailable()) { $defaultValue = $parameter->getDefaultValue(); @@ -251,6 +270,21 @@ class Piwik_API_Proxy $this->metadataArray[$class][$name]['numberOfRequiredParameters'] = $method->getNumberOfRequiredParameters(); } } + + /** + * Checks that the method exists in the class + * + * @param string The class name + * @param string The method name + * @throws exception If the method is not found + */ + private function checkMethodExists($className, $methodName) + { + if(!$this->isMethodAvailable($className, $methodName)) + { + throw new Exception("The method '$methodName' does not exist or is not available in the module '".$className."'."); + } + } /** * Returns the number of required parameters (parameters without default values). @@ -277,9 +311,6 @@ class Piwik_API_Proxy } /** - * Checks that all given parameters are not null - * - * @param array of values * @throws exception If any parameter value is null */ private function checkParametersAreNotNull($className, $methodName, $parametersValues) @@ -307,19 +338,4 @@ class Piwik_API_Proxy throw new Exception("Objects that provide an API must be Singleton and have a 'static public function getInstance()' method."); } } - - /** - * Returns the API class name given the module name. - * - * For exemple for $module = 'Referers' it returns 'Piwik_Referers_API' - * Piwik_Referers_API contains the methods to be published in the API. - * - * @param string module name - * @return string class name - */ - private function getClassNameFromModule($module) - { - $class = Piwik::prefixClass($module ."_API"); - return $class; - } } diff --git a/core/API/Request.php b/core/API/Request.php index 55404c5a2f..aea6371410 100644 --- a/core/API/Request.php +++ b/core/API/Request.php @@ -11,6 +11,7 @@ */ require_once "API/ResponseBuilder.php"; +require_once "API/DataTableGenericFilter.php"; /** * An API request is the object used to make a call to the API and get the result. @@ -108,23 +109,10 @@ class Piwik_API_Request { throw new Exception_PluginDeactivated($module); } - $className = "Piwik_" . $module . "_API"; + $module = "Piwik_" . $module . "_API"; - // call the method via the API_Proxy class - $api = Piwik_Api_Proxy::getInstance(); - $api->registerClass($module); - - // check method exists - $api->checkMethodExists($className, $method); - - // get the list of parameters required by the method - $parameters = $api->getParametersList($className, $method); - - // load the parameters from the request URL - $finalParameters = $this->getRequestParametersArray( $parameters ); - // call the method - $returnedValue = call_user_func_array( array( $api->$module, $method), $finalParameters ); + $returnedValue = Piwik_Api_Proxy::getInstance()->call($module, $method, $this->request); $toReturn = $response->getResponse($returnedValue); } catch(Exception $e ) { @@ -132,39 +120,6 @@ class Piwik_API_Request } return $toReturn; } - - /** - * Returns an array containing the values of the parameters to pass to the method to call - * - * @param array array of (parameter name, default value) - * @return array values to pass to the function call - * @throws exception If there is a parameter missing from the required function parameters - */ - protected function getRequestParametersArray( $parameters ) - { - $finalParameters = array(); - foreach($parameters as $name => $defaultValue) - { - try{ - if($defaultValue === Piwik_API_Proxy::NO_DEFAULT_VALUE) - { - try { - $requestValue = Piwik_Common::getRequestVar($name, null, null, $this->request); - } catch(Exception $e) { - $requestValue = null; - } - } - else - { - $requestValue = Piwik_Common::getRequestVar($name, $defaultValue, null, $this->request); - } - } catch(Exception $e) { - throw new Exception("The required variable '$name' is not correct or has not been found in the API Request. Add the parameter '&$name=' (with a value) in the URL."); - } - $finalParameters[] = $requestValue; - } - return $finalParameters; - } /** * Returns array( $class, $method) from the given string $class.$method diff --git a/core/API/ResponseBuilder.php b/core/API/ResponseBuilder.php index d66cdf9679..2a97febdb0 100644 --- a/core/API/ResponseBuilder.php +++ b/core/API/ResponseBuilder.php @@ -124,72 +124,6 @@ class Piwik_API_ResponseBuilder return $return; } - - /** - * Returns an array containing the information of the generic Piwik_DataTable_Filter - * to be applied automatically to the data resulting from the API calls. - * - * Order to apply the filters: - * 1 - Filter that remove filtered rows - * 2 - Filter that sort the remaining rows - * 3 - Filter that keep only a subset of the results - * 4 - Presentation filters - * - * @return array See the code for spec - */ - public static function getGenericFiltersInformation() - { - $genericFilters = array( - 'Pattern' => array( - 'filter_column' => array('string'), - 'filter_pattern' => array('string'), - ), - 'PatternRecursive' => array( - 'filter_column_recursive' => array('string'), - 'filter_pattern_recursive' => array('string'), - ), - 'ExactMatch' => array( - 'filter_exact_column' => array('string'), - 'filter_exact_pattern' => array('array'), - ), - 'ExcludeLowPopulation' => array( - 'filter_excludelowpop' => array('string'), - 'filter_excludelowpop_value'=> array('float'), - ), - 'AddColumnsWhenShowAllColumns' => array( - 'filter_add_columns_when_show_all_columns' => array('integer') - ), - 'Sort' => array( - 'filter_sort_column' => array('string', 'nb_visits'), - 'filter_sort_order' => array('string', Zend_Registry::get('config')->General->dataTable_default_sort_order), - ), - 'Limit' => array( - 'filter_offset' => array('integer', '0'), - 'filter_limit' => array('integer', Zend_Registry::get('config')->General->dataTable_default_limit), - ), - 'SafeDecodeLabel' => array(), - ); - - return $genericFilters; - } - - protected function handleDataTableGenericFilters($datatable) - { - if($datatable instanceof Piwik_DataTable) - { - $this->applyDataTableGenericFilters($datatable); - } - elseif($datatable instanceof Piwik_DataTable_Array) - { - $tables = $datatable->getArray(); - foreach($tables as $table) - { - $this->applyDataTableGenericFilters($table); - } - } - return $datatable; - } - /** * Returns true if the user requested to serialize the output data (&serialize=1 in the request) * @@ -313,7 +247,8 @@ class Piwik_API_ResponseBuilder // if the flag disable_generic_filters is defined we skip the generic filters if(Piwik_Common::getRequestVar('disable_generic_filters', 'false', 'string', $this->request) == 'false') { - $datatable = $this->handleDataTableGenericFilters($datatable); + $genericFilter = new Piwik_API_DataTableGenericFilter($datatable, $this->request); + $genericFilter->filter(); } // if the flag disable_queued_filters is defined we skip the filters that were queued @@ -345,73 +280,4 @@ class Piwik_API_ResponseBuilder } } - /** - * Apply generic filters to the DataTable object resulting from the API Call. - * Disable this feature by setting the parameter disable_generic_filters to 1 in the API call request. - * - * @param Piwik_DataTable - * @return void - */ - protected function applyDataTableGenericFilters($dataTable) - { - if($dataTable instanceof Piwik_DataTable_Array ) - { - $tables = $dataTable->getArray(); - foreach($tables as $table) - { - $this->applyDataTableGenericFilters($table); - } - return; - } - - $genericFilters = self::getGenericFiltersInformation(); - - foreach($genericFilters as $filterName => $parameters) - { - $filterParameters = array(); - $exceptionRaised = false; - - foreach($parameters as $name => $info) - { - // parameter type to cast to - $type = $info[0]; - - // default value if specified, when the parameter doesn't have a value - $defaultValue = null; - if(isset($info[1])) - { - $defaultValue = $info[1]; - } - - try { - $value = Piwik_Common::getRequestVar($name, $defaultValue, $type, $this->request); - settype($value, $type); - $filterParameters[] = $value; - } - catch(Exception $e) - { - $exceptionRaised = true; - break; - } - } - - if(!$exceptionRaised) - { - // a generic filter class name must follow this pattern - $class = "Piwik_DataTable_Filter_".$filterName; - - if($filterName == 'Limit') - { - $dataTable->setRowsCountBeforeLimitFilter(); - } - - // build the set of parameters for the filter - $filterParameters = array_merge(array($dataTable), $filterParameters); - - // use Reflection to create a new instance of the filter, given parameters $filterParameters - $reflectionObj = new ReflectionClass($class); - $filter = $reflectionObj->newInstanceArgs($filterParameters); - } - } - } } diff --git a/core/ViewDataTable.php b/core/ViewDataTable.php index 224e627e60..164e5066dc 100644 --- a/core/ViewDataTable.php +++ b/core/ViewDataTable.php @@ -10,6 +10,7 @@ */ require_once "API/Request.php"; +require_once "API/FilterDataTable.php"; /** * This class is used to load (from the API) and customize the output of a given DataTable. @@ -36,7 +37,6 @@ require_once "API/Request.php"; * @package Piwik_ViewDataTable * */ - abstract class Piwik_ViewDataTable { /** @@ -422,7 +422,7 @@ abstract class Piwik_ViewDataTable * - etc. * * The values are loaded: - * - from the generic filters that are applied by default @see Piwik_API_ResponseBuilder::getGenericFiltersInformation() + * - from the generic filters that are applied by default @see Piwik_API_DataTableGenericFilter.php::getGenericFiltersInformation() * - from the values already available in the GET array * - from the values set using methods from this class (eg. setSearchPattern(), setLimit(), etc.) * @@ -433,7 +433,7 @@ abstract class Piwik_ViewDataTable // build javascript variables to set $javascriptVariablesToSet = array(); - $genericFilters = Piwik_API_ResponseBuilder::getGenericFiltersInformation(); + $genericFilters = Piwik_API_DataTableGenericFilter::getGenericFiltersInformation(); foreach($genericFilters as $filter) { foreach($filter as $filterVariableName => $filterInfo) diff --git a/plugins/API/Controller.php b/plugins/API/Controller.php index 2b21cd9883..5bac1b2ce9 100644 --- a/plugins/API/Controller.php +++ b/plugins/API/Controller.php @@ -35,29 +35,11 @@ class Piwik_API_Controller extends Piwik_Controller { $view = new Piwik_View("API/templates/listAllAPI.tpl"); $this->setGeneralVariablesView($view); - $view->countLoadedAPI = $this->init(); + $ApiDocumentation = new Piwik_API_DocumentationGenerator(); + $view->countLoadedAPI = Piwik_API_Proxy::getInstance()->getCountRegisteredClasses(); $view->list_api_methods_with_links = $ApiDocumentation->getAllInterfaceString(); echo $view->render(); } - - protected function init() - { - $plugins = Piwik_PluginsManager::getInstance()->getLoadedPluginsName(); - - $loaded = 0; - foreach( $plugins as $plugin ) - { - $plugin = Piwik::unprefixClass($plugin); - - try { - Piwik_API_Proxy::getInstance()->registerClass($plugin); - $loaded++; - } - catch(Exception $e){ - } - } - return $loaded; - } } |