Answers.Body: <p>I find the 'normal' way pretty ugly.</p>
  <ul>
  <li>listing the args by name in an array, then</li>
  <li>supplying the args, then</li>
  <li>needing to pass by ref a NULL/dummy variable for each of the remaining 6 or was it 7? arguments - this is ugly and error prone and passing <code>CRM_Core_DAO::$_nullObject</code> is risky as it allows the hook to overwrite that so it's no longer NULL.</li>
  </ul>
  <p>I think the following can be a nicer approach, here's an example:</p>
  <h2>Fire an event</h2>
  <pre><code>// Create an event object with all the data you wan to pass in.
  $event = Civi\Core\Event\GenericHookEvent::create(
    ['fname' =&gt; $fname, 'votes' =&gt; &amp;$votes]);
  Civi::dispatcher()-&gt;dispatch('civi.something.you.make.up', $event);
  </code></pre>
  <h2>Example listener</h2>
  <pre><code>function your_listener_callable($event) {
    print &quot;running &quot; . __FUNCTION__ . &quot;\n&quot;;
    // We can overwrite data
    $event-&gt;fname = 'Wilma Flintstone';
    // We can alter anything passed by reference.
    $event-&gt;votes++;
    // To return a value you need to pass it in an array like so:
    $event-&gt;addReturnValues(['Successfully recorded votes!']);
    // We can't write any properties that the event did
    // not originally have defined, so this won't work:
    $event-&gt;friend = 'Barney';
  }
  </code></pre>
  <h2>Use data resulting from the event</h2>
  <p>This code would come directly after the 'Fire an event' code.</p>
  <pre><code>print &quot;$fname ($event-&gt;fname) now has $votes votes.\n&quot;;
  $return = $event-&gt;getReturnValues();
  print &quot;The hook returned: $return[0]\n&quot;;
  // $event-&gt;friend === NULL
  </code></pre>
  <h2>Link your listener to the event.</h2>
  <p>You'd normally do this in a <code>hook_civicrm_container()</code> function to ensure it is only added once.</p>
  <pre><code>/**
   * Implements hook_civicrm_container().
   *
   * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_container/
   */
  function myext_civicrm_container($container) {
    // https://docs.civicrm.org/dev/en/latest/hooks/usage/symfony/
    $container-&gt;findDefinition('dispatcher')
    -&gt;addMethodCall('addListener', [
      'civi.something.you.made.up',
      'your_listener_callable']);
  }
  </code></pre>
  <h2>Full working command line example</h2>
  <p>You can run this with <code>cv scr hookexample.php</code></p>
  <pre><code>&lt;?php
  use \Civi\Core\Event\GenericHookEvent;
  
  if (php_sapi_name() !== 'cli') {
    http_response_code(404);
    exit;
  }
  
  function your_listener_callable($event) {
    print &quot;running &quot; . __FUNCTION__ . &quot;\n&quot;;
    // We can overwrite data passed by value, but that's probably confusing.
    $event-&gt;fname = 'Wilma Flintstone';
    // We can alter anything passed by reference.
    $event-&gt;votes++;
    // To return a value you need to pass it in an array like so:
    $event-&gt;addReturnValues(['Successfully recorded votes!']);
    // We can't write any properties that the event did not originally 
    // have defined, so this won't work:
    $event-&gt;friend = 'Barney';
  }
  
  // As this is a single-pass script we can just do this:
  Civi::dispatcher()-&gt;addListener('civi.something.you.make.up', 'your_listener_callable');
  
  $votes = 0;
  $fname = 'Wilma';
  print &quot;$fname has $votes votes.\n&quot;;
  
  $event = GenericHookEvent::create(['fname' =&gt; 'Wilma', 'votes' =&gt; &amp;$votes]);
  Civi::dispatcher()-&gt;dispatch('civi.something.you.make.up', $event);
  
  $return = $event-&gt;getReturnValues();
  
  print &quot;$fname ($event-&gt;fname) now has $votes votes.\n&quot;;
  print &quot;The hook returned: $return[0]\n&quot;;
  // $event-&gt;friend is NULL.
  </code></pre>
  <p>Outputs:</p>
  <pre><code>Wilma has 0 votes.
  running your_listener_callable
  Wilma (Wilma Flintstone) now has 1 votes.
  The hook returned: Successfully recorded votes!
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-07-09T16:16:48.770Z
Answers.LastActivityDate: 2021-07-12T14:42:52.993Z
Answers.LastEditDate: 2021-07-12T14:42:52.993Z
Answers.UserDisplayName: artfulrobot
Answers.Body: <blockquote>
  <h2>EDIT</h2>
  <p>This answer is now deprecated, you should use <a href="https://civicrm.stackexchange.com/a/30933/35">@bgm's answer</a> instead, or for an alternative and possibly cleaner approach, try <a href="https://civicrm.stackexchange.com/a/39948/35">my other answer</a></p>
  </blockquote>
  <p>All thanks to the code example link in <a href="https://civicrm.stackexchange.com/a/25083/35">Eric's answer</a> here's a simplified example of how to offer up a hook (<code>my_new_hook</code>) which takes one argument, <code>$the_argument</code>:</p>
  <pre><code>CRM_Utils_Hook::singleton()-&gt;invoke(
    1, $the_argument,
    $dummy, $dummy, $dummy, $dummy, $dummy,
    'my_new_hook');                                                                      
  </code></pre>
  <p>Then other extensions can include functions like</p>
  <pre><code>function myextension_my_new_hook($arg) {
    do_something_with($arg);
  }
  </code></pre>
  <p>The <code>$dummy</code> is just because we must pass a variable (since it's passed by reference), but this hook does not need it. If you had a hook that took two arguments, you'd use <code>2</code> in place of <code>1</code> and replace the first <code>$dummy</code> with your actual second argument etc. up to 6.</p>
  <p>Nb. this code assumes CiviCRM 4.5+, but obviously you're not using anything older than that these days, right?!</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-04T08:06:27.727Z
Answers.Text: Standards are important, I agree. However in this case it is a little odd as the reason parameters have to be passed in this way is because they are passed by reference so that the function receiving them can change them. Passing a static variable could result in that static var being changed, so I think the way I've done it is safer as well as clearer and more succinct.
Answers.UserDisplayName: artfulrobot
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-05T07:53:27.363Z
Answers.Text: Ah I have never thought about your reasning and that makes sense.
Answers.UserDisplayName: Jaap Jansma - CiviCooP
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-05-24T14:19:04.890Z
Answers.Text: Instead of `$dummy` you could use `CRM_Core_DAO::$_nullObject`
Answers.UserDisplayName: Jaap Jansma - CiviCooP
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-05-25T13:41:47.700Z
Answers.Text: Yep, but not sure what the advantage of that is?
Answers.UserDisplayName: artfulrobot
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-05-28T07:22:43.600Z
Answers.Text: There is no advantage except that is somehow a standard way of doing such things in CiviCRM.
Answers.UserDisplayName: Jaap Jansma - CiviCooP
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-10-08T18:40:25.270Z
Answers.Text: Note the `invoke()` syntax with the first parameter being an integer is now deprecated.  See the named parameter example from @bgm
Answers.UserDisplayName: Aidan
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-05-24T10:45:24.723Z
Answers.LastActivityDate: 2021-07-12T12:42:47.960Z
Answers.LastEditDate: 2021-07-12T12:42:47.960Z
Answers.UserDisplayName: artfulrobot
Answers.Body: <p>Here is an example using the named parameter syntax:</p>
  
  <pre><code>CRM_Utils_Hook::singleton()-&gt;invoke(
    ['pdf', 'pdf_variables', 'receipt'],
    $pdf, $pdf_variables, $receipt,
    CRM_Utils_Hook::$_nullObject, CRM_Utils_Hook::$_nullObject, CRM_Utils_Hook::$_nullObject,
    'cdntaxreceipts_writeReceipt');
  </code></pre>
  
  <p>One advantage of this syntax, is that extension can then implement the hook with the Symfony syntax. When managed by Symfony, it's easier to change the priority or add/remove hooks from other extensions.</p>
  
  <p>Example:</p>
  
  <pre><code>/**
   * Implements hook_civicrm_container().
   *
   * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_container/
   */
  function myext_civicrm_container($container) {
    // https://docs.civicrm.org/dev/en/latest/hooks/usage/symfony/
    $container-&gt;findDefinition('dispatcher')
      -&gt;addMethodCall('addListener', ['hook_cdntaxreceipts_writeReceipt', 'myext_cdntaxreceipts_symfony_writeReceipt']);
  }
  
  /**
   * Implements hook_cdntaxreceipts_writeReceipt() via Symfony.
   *
   * @see myext_civicrm_container()
   */
  function myext_cdntaxreceipts_symfony_writeReceipt($event) {
    // Access the parameters with:
    // $event-&gt;pdf;
    // $event-&gt;pdf_variables;
    // $event-&gt;receipt;
  }
  </code></pre>
  
  <p>Note that the implementing function <code>myext_cdntaxreceipts_symfony_writeReceipt</code> has <code>symfony</code> in the name only to avoid being called by the traditional hook system. We can name the function however we want.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-06-05T22:22:54.900Z
Answers.LastActivityDate: 2019-10-22T14:57:47.700Z
Answers.LastEditDate: 2019-10-22T14:57:47.700Z
Answers.UserDisplayName: bgm
Answers.Body: <p>Here is an example of a hook we created for CiviRules:</p>
  
  <p><a href="https://lab.civicrm.org/extensions/civirules/blob/master/CRM/Civirules/Utils/HookInvoker.php" rel="nofollow noreferrer">https://lab.civicrm.org/extensions/civirules/blob/master/CRM/Civirules/Utils/HookInvoker.php</a></p>
  
  <p>Hope this helps :-)</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-11-06T16:38:08.130Z
Answers.Text: In case you have not noticed, your link is now broken and I am not sure the repository is still around.
Answers.UserDisplayName: Javier Elices
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-11-07T09:47:34.720Z
Answers.Text: The repository moved to GitLab so should now be https://lab.civicrm.org/extensions/civirules/blob/master/CRM/Civirules/Utils/HookInvoker.php
Answers.UserDisplayName: ErikH - CiviCooP
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-05-24T10:00:28.553Z
Answers.LastActivityDate: 2018-11-07T15:50:40.680Z
Answers.LastEditDate: 2018-11-07T15:50:40.680Z
Answers.UserDisplayName: ErikH - CiviCooP
Body: <p>I want to give others the chance to do something when certain events happen in my extension.</p>
  
  <p>So to be clear: I'm not wanting to use existing hooks in my extension, I'm wanting to create a new hook that my extension will invoke and other code may choose to react to.</p>
  
  <p>I couldn't find anything in the <a href="https://docs.civicrm.org/dev/en/latest/hooks" rel="nofollow noreferrer">developers' guide</a> about this. Looking at the source makes me think that maybe I should be doing something with <code>CRM_Utils_Hook</code> or perhaps <code>\Civi::dispatcher()-&gt;dispatch('hook_my_new_hook', $some_event_object)</code>?</p>
  
  <p>Is there a standardised way to do this? Are there any good examples?</p>
  
Tags: <extension><developer><hooks>
Title: How to create a hook in an extension?
ThreadText: # How to create a hook in an extension?
  
  (Asked by: username_0 on 2018-05-24)
  
  I want to give others the chance to do something when certain events happen in my extension.
  
  So to be clear: I'm not wanting to use existing hooks in my extension, I'm wanting to create a new hook that my extension will invoke and other code may choose to react to.
  
  I couldn't find anything in the [developers' guide](https://docs.civicrm.org/dev/en/latest/hooks) about this. Looking at the source makes me think that maybe I should be doing something with `CRM_Utils_Hook` or perhaps `\Civi::dispatcher()->dispatch('hook_my_new_hook', $some_event_object)`?
  
  Is there a standardised way to do this? Are there any good examples?
  
  **Tags:** extension, developer, hooks
  
  
  ---
  
  ## 4 answers
  
  ### Answer by username_0 on 2021-07-09. Score: 1 | Accepted answer
  
  I find the 'normal' way pretty ugly.
  
  * listing the args by name in an array, then
  * supplying the args, then
  * needing to pass by ref a NULL/dummy variable for each of the remaining 6 or was it 7? arguments - this is ugly and error prone and passing `CRM_Core_DAO::$_nullObject` is risky as it allows the hook to overwrite that so it's no longer NULL.
  
  I think the following can be a nicer approach, here's an example:
  
  ## Fire an event
  
  ```
  // Create an event object with all the data you wan to pass in.
  $event = Civi\Core\Event\GenericHookEvent::create(
    ['fname' => $fname, 'votes' => &$votes]);
  Civi::dispatcher()->dispatch('civi.something.you.make.up', $event);
  
  ```
  
  ## Example listener
  
  ```
  function your_listener_callable($event) {
    print "running " . __FUNCTION__ . "\n";
    // We can overwrite data
    $event->fname = 'Wilma Flintstone';
    // We can alter anything passed by reference.
    $event->votes++;
    // To return a value you need to pass it in an array like so:
    $event->addReturnValues(['Successfully recorded votes!']);
    // We can't write any properties that the event did
    // not originally have defined, so this won't work:
    $event->friend = 'Barney';
  }
  
  ```
  
  ## Use data resulting from the event
  
  This code would come directly after the 'Fire an event' code.
  
  ```
  print "$fname ($event->fname) now has $votes votes.\n";
  $return = $event->getReturnValues();
  print "The hook returned: $return[0]\n";
  // $event->friend === NULL
  
  ```
  
  ## Link your listener to the event.
  
  You'd normally do this in a `hook_civicrm_container()` function to ensure it is only added once.
  
  ```
  /**
   * Implements hook_civicrm_container().
   *
   * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_container/
   */
  function myext_civicrm_container($container) {
    // https://docs.civicrm.org/dev/en/latest/hooks/usage/symfony/
    $container->findDefinition('dispatcher')
    ->addMethodCall('addListener', [
      'civi.something.you.made.up',
      'your_listener_callable']);
  }
  
  ```
  
  ## Full working command line example
  
  You can run this with `cv scr hookexample.php`
  
  ```
  <?php
  use \Civi\Core\Event\GenericHookEvent;
  
  if (php_sapi_name() !== 'cli') {
    http_response_code(404);
    exit;
  }
  
  function your_listener_callable($event) {
    print "running " . __FUNCTION__ . "\n";
    // We can overwrite data passed by value, but that's probably confusing.
    $event->fname = 'Wilma Flintstone';
    // We can alter anything passed by reference.
    $event->votes++;
    // To return a value you need to pass it in an array like so:
    $event->addReturnValues(['Successfully recorded votes!']);
    // We can't write any properties that the event did not originally 
    // have defined, so this won't work:
    $event->friend = 'Barney';
  }
  
  // As this is a single-pass script we can just do this:
  Civi::dispatcher()->addListener('civi.something.you.make.up', 'your_listener_callable');
  
  $votes = 0;
  $fname = 'Wilma';
  print "$fname has $votes votes.\n";
  
  $event = GenericHookEvent::create(['fname' => 'Wilma', 'votes' => &$votes]);
  Civi::dispatcher()->dispatch('civi.something.you.make.up', $event);
  
  $return = $event->getReturnValues();
  
  print "$fname ($event->fname) now has $votes votes.\n";
  print "The hook returned: $return[0]\n";
  // $event->friend is NULL.
  
  ```
  
  Outputs:
  
  ```
  Wilma has 0 votes.
  running your_listener_callable
  Wilma (Wilma Flintstone) now has 1 votes.
  The hook returned: Successfully recorded votes!
  
  ```
  
  
  ---
  
  ### Answer by username_0 on 2018-05-24. Score: 7
  
  > ## EDIT
  >
  > This answer is now deprecated, you should use [@username_3's answer](https://civicrm.stackexchange.com/a/30933/35) instead, or for an alternative and possibly cleaner approach, try [my other answer](https://civicrm.stackexchange.com/a/39948/35)
  
  All thanks to the code example link in [Eric's answer](https://civicrm.stackexchange.com/a/25083/35) here's a simplified example of how to offer up a hook (`my_new_hook`) which takes one argument, `$the_argument`:
  
  ```
  CRM_Utils_Hook::singleton()->invoke(
    1, $the_argument,
    $dummy, $dummy, $dummy, $dummy, $dummy,
    'my_new_hook');                                                                      
  
  ```
  
  Then other extensions can include functions like
  
  ```
  function myextension_my_new_hook($arg) {
    do_something_with($arg);
  }
  
  ```
  
  The `$dummy` is just because we must pass a variable (since it's passed by reference), but this hook does not need it. If you had a hook that took two arguments, you'd use `2` in place of `1` and replace the first `$dummy` with your actual second argument etc. up to 6.
  
  Nb. this code assumes CiviCRM 4.5+, but obviously you're not using anything older than that these days, right?!
  
  > **Comments:**
  > **username_1**: Instead of `$dummy` you could use `CRM_Core_DAO::$_nullObject`  
  > **username_0**: Yep, but not sure what the advantage of that is?  
  > **username_1**: There is no advantage except that is somehow a standard way of doing such things in CiviCRM.  
  > **username_0**: Standards are important, I agree. However in this case it is a little odd as the reason parameters have to be passed in this way is because they are passed by reference so that the function receiving them can change them. Passing a static variable could result in that static var being changed, so I think the way I've done it is safer as well as clearer and more succinct.  
  > **username_1**: Ah I have never thought about your reasning and that makes sense.  
  > **username_2**: Note the `invoke()` syntax with the first parameter being an integer is now deprecated.  See the named parameter example from @username_3  
  
  ---
  
  ### Answer by username_3 on 2019-06-05. Score: 5
  
  Here is an example using the named parameter syntax:
  
  ```
  CRM_Utils_Hook::singleton()->invoke(
    ['pdf', 'pdf_variables', 'receipt'],
    $pdf, $pdf_variables, $receipt,
    CRM_Utils_Hook::$_nullObject, CRM_Utils_Hook::$_nullObject, CRM_Utils_Hook::$_nullObject,
    'cdntaxreceipts_writeReceipt');
  
  ```
  
  One advantage of this syntax, is that extension can then implement the hook with the Symfony syntax. When managed by Symfony, it's easier to change the priority or add/remove hooks from other extensions.
  
  Example:
  
  ```
  /**
   * Implements hook_civicrm_container().
   *
   * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_container/
   */
  function myext_civicrm_container($container) {
    // https://docs.civicrm.org/dev/en/latest/hooks/usage/symfony/
    $container->findDefinition('dispatcher')
      ->addMethodCall('addListener', ['hook_cdntaxreceipts_writeReceipt', 'myext_cdntaxreceipts_symfony_writeReceipt']);
  }
  
  /**
   * Implements hook_cdntaxreceipts_writeReceipt() via Symfony.
   *
   * @see myext_civicrm_container()
   */
  function myext_cdntaxreceipts_symfony_writeReceipt($event) {
    // Access the parameters with:
    // $event->pdf;
    // $event->pdf_variables;
    // $event->receipt;
  }
  
  ```
  
  Note that the implementing function `myext_cdntaxreceipts_symfony_writeReceipt` has `symfony` in the name only to avoid being called by the traditional hook system. We can name the function however we want.
  
  
  ---
  
  ### Answer by username_4 on 2018-05-24. Score: 5
  
  Here is an example of a hook we created for CiviRules:
  
  <https://lab.civicrm.org/extensions/civirules/blob/master/CRM/Civirules/Utils/HookInvoker.php>
  
  Hope this helps :-)
  
  > **Comments:**
  > **username_5**: In case you have not noticed, your link is now broken and I am not sure the repository is still around.  
  > **username_4**: The repository moved to GitLab so should now be https://lab.civicrm.org/extensions/civirules/blob/master/CRM/Civirules/Utils/HookInvoker.php  
  
  ---
  
  