Merge pull request #15760 from colemanw/iconPicker
[civicrm-core.git] / Civi / Api4 / Generic / AbstractAction.php
index d81c9aa33a20e481c013b8c0de8920694cde85db..310df486e5c4ca14b8573e2d0f0918677167672b 100644 (file)
@@ -26,8 +26,19 @@ use Civi\Api4\Utils\ActionUtil;
 /**
  * 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()
  */
@@ -70,6 +81,18 @@ abstract class AbstractAction implements \ArrayAccess {
    */
   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
    */
@@ -107,6 +130,8 @@ abstract class AbstractAction implements \ArrayAccess {
    */
   private $_id;
 
+  public $_debugOutput = [];
+
   /**
    * Action constructor.
    *
@@ -140,7 +165,7 @@ abstract class AbstractAction implements \ArrayAccess {
    * @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;
@@ -161,7 +186,7 @@ abstract class AbstractAction implements \ArrayAccess {
   }
 
   /**
-   * Magic function to provide addFoo, getFoo and setFoo for params.
+   * Magic function to provide automatic getter/setter for params.
    *
    * @param $name
    * @param $arguments
@@ -174,10 +199,6 @@ abstract class AbstractAction implements \ArrayAccess {
       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':
@@ -186,18 +207,6 @@ abstract class AbstractAction implements \ArrayAccess {
         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);
@@ -210,13 +219,21 @@ abstract class AbstractAction implements \ArrayAccess {
    * 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;
   }
 
   /**
@@ -383,12 +400,13 @@ abstract class AbstractAction implements \ArrayAccess {
   /**
    * 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() {
@@ -462,4 +480,25 @@ abstract class AbstractAction implements \ArrayAccess {
     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);
+      }
+    }
+  }
+
 }