/**
* Base class for all api actions.
*
- * @method $this setCheckPermissions(bool $value)
+ * An api Action object stores the parameters of the api call, and defines a _run function to execute the action.
+ *
+ * Every `protected` class var is considered a parameter (unless it starts with an underscore).
+ *
+ * Adding a `protected` var to your Action named e.g. `$thing` will automatically:
+ * - Provide a getter/setter (via `__call` MagicMethod) named `getThing()` and `setThing()`.
+ * - Expose the param in the Api Explorer (be sure to add a doc-block as it displays in the help panel).
+ * - Require a value for the param if you add the "@required" annotation.
+ *
+ * @method $this setCheckPermissions(bool $value) Enable/disable permission checks
* @method bool getCheckPermissions()
+ * @method $this setDebug(bool $value) Enable/disable debug output
+ * @method bool getDebug()
* @method $this setChain(array $chain)
* @method array getChain()
*/
*/
protected $checkPermissions = TRUE;
+ /**
+ * Add debugging info to the api result.
+ *
+ * When enabled, the $result->debug will be populated with information about the api call,
+ * including sql queries executed.
+ *
+ * Note: with checkPermissions enabled, debug info will only be returned if the user has "view debug output" permission.
+ *
+ * @var bool
+ */
+ protected $debug = FALSE;
+
/**
* @var string
*/
*/
private $_id;
+ public $_debugOutput = [];
+
/**
* Action constructor.
*
* @throws \API_Exception
*/
public function setVersion($val) {
- if ($val != 4) {
+ if ($val !== 4 && $val !== '4') {
throw new \API_Exception('Cannot modify api version');
}
return $this;
}
/**
- * Magic function to provide addFoo, getFoo and setFoo for params.
+ * Magic function to provide automatic getter/setter for params.
*
* @param $name
* @param $arguments
throw new \API_Exception('Unknown api parameter: ' . $name);
}
$mode = substr($name, 0, 3);
- // Handle plural when adding to e.g. $values with "addValue" method.
- if ($mode == 'add' && $this->paramExists($param . 's')) {
- $param .= 's';
- }
if ($this->paramExists($param)) {
switch ($mode) {
case 'get':
case 'set':
$this->$param = $arguments[0];
return $this;
-
- case 'add':
- if (!is_array($this->$param)) {
- throw new \API_Exception('Cannot add to non-array param');
- }
- if (array_key_exists(1, $arguments)) {
- $this->{$param}[$arguments[0]] = $arguments[1];
- }
- else {
- $this->{$param}[] = $arguments[0];
- }
- return $this;
}
}
throw new \API_Exception('Unknown api parameter: ' . $name);
* This is basically the outer wrapper for api v4.
*
* @return \Civi\Api4\Generic\Result
+ * @throws \API_Exception
* @throws \Civi\API\Exception\UnauthorizedException
*/
public function execute() {
/** @var \Civi\API\Kernel $kernel */
$kernel = \Civi::service('civi_api_kernel');
-
- return $kernel->runRequest($this);
+ $result = $kernel->runRequest($this);
+ if ($this->debug && (!$this->checkPermissions || \CRM_Core_Permission::check('view debug output'))) {
+ $result->debug['actionClass'] = get_class($this);
+ $result->debug = array_merge($result->debug, $this->_debugOutput);
+ }
+ else {
+ $result->debug = NULL;
+ }
+ return $result;
}
/**
/**
* Returns schema fields for this entity & action.
*
- * Here we bypass the api wrapper and execute the getFields action directly.
+ * Here we bypass the api wrapper and run the getFields action directly.
* This is because we DON'T want the wrapper to check permissions as this is an internal op,
* but we DO want permissions to be checked inside the getFields request so e.g. the api_key
* field can be conditionally included.
* @see \Civi\Api4\Action\Contact\GetFields
*
+ * @throws \API_Exception
* @return array
*/
public function entityFields() {
return (bool) trim(\CRM_Core_Smarty::singleton()->fetchWith('string:' . $tpl, $vars));
}
+ /**
+ * When in debug mode, this logs the callback function being used by a Basic*Action class.
+ *
+ * @param callable $callable
+ */
+ protected function addCallbackToDebugOutput($callable) {
+ if ($this->debug && empty($this->_debugOutput['callback'])) {
+ if (is_scalar($callable)) {
+ $this->_debugOutput['callback'] = (string) $callable;
+ }
+ elseif (is_array($callable)) {
+ foreach ($callable as $key => $unit) {
+ $this->_debugOutput['callback'][$key] = is_object($unit) ? get_class($unit) : (string) $unit;
+ }
+ }
+ elseif (is_object($callable)) {
+ $this->_debugOutput['callback'] = get_class($callable);
+ }
+ }
+ }
+
}