diff --git a/docs/dev-tools/debugging.md b/docs/dev-tools/debugging.md
index 6520c6b9225e97d418a9896d2e6e6aee3173767a..775cf9404a0dbbd1a66bc7050b89f649dfd5b263 100644
--- a/docs/dev-tools/debugging.md
+++ b/docs/dev-tools/debugging.md
@@ -8,7 +8,7 @@ also instructions on setting up XDebug, which is the recommended
 debugging tool for CiviCRM when you have bugs which are really hard to squish.
 
 !!! danger "Security Alert"
-    None of these debugging should be performed on production sites, as they can expose system configuration and authentication information being to unauthorized visitors.
+    None of these debugging methods should be performed on production sites, as they can expose system configuration and authentication information to unauthorized visitors.
 
 The debugging methods presented here are ordered with the easiest ones first, but you may find the more challenging methods near the end to be more rewarding.
 
@@ -34,7 +34,7 @@ After enabling debugging, append any of the following name-value pairs to the UR
 -   `&directoryCleanup=3` performs both of the above actions.
 -   `&backtrace=1` displays a stack trace listing at the top of a page.
 -   `&sessionDebug=1` displays the current users session variables in the browser.
--   `&angularDebug=1` displays live variable updates on certain Angular-based pages
+-   `&angularDebug=1` displays live variable updates on certain Angular-based pages.
 
 !!! tip "Caveats"
     - Sometimes using `&smartyDebug=1` to inspect variables available to a template will not work as expected.  An example of this is looking at the Contact Summary page, when using this method will display the variables available only to the summary tab and you might want to see the variables available to one of the other tabs. To do this you will need to debug via code, as explained below.
@@ -72,7 +72,7 @@ The following values can be added to your site's settings file `civicrm.settings
 
 - `define('CIVICRM_DAO_DEBUG', 1);` writes out various data layer queries to your browser screen.
 
-- `define('CIVICRM_CONTAINER_CACHE', 'never');` causes Civi to rebuild the [container](http://symfony.com/doc/current/service_container.html) from the latest data on every page-load
+- `define('CIVICRM_CONTAINER_CACHE', 'never');` causes Civi to rebuild the [container](http://symfony.com/doc/current/service_container.html) from the latest data on every page-load.
 
 !!! tip
     When any sort of "logging stuff to a file" is enabled by one of the
@@ -277,3 +277,74 @@ From there you can step through the code to the part you're interested
 in. But it's probably a better idea to set a breakpoint in the part of
 the code you're interested in stopping at. See the MacGDBp documentation
 for more information.
+
+## Error 500, white screen (WSoD),  "Internal Server Error" errors
+
+If CiviCRM shows a blank white page or page with "Error 500" with no other messages on screen, follow the steps in this section to diagnose and fix. A white screen (WSoD or "white screen of death") indicates that PHP is configured not to display errors, and has hit an error which it can't recover from. The result is an empty page. 
+
+Your next step is not to fix the error, but to first give yourself enough information to identify the source of the error. (Diagnose, then treat.)
+
+**Viewing errors in logfiles**
+
+The webserver can be configured to display errors to screen, but it also logs errors to files on disk. These files vary depending on your hosting environment, so you might consult your webhost's documentation to locate them. You might look for errors in some of these locations depending on webserver/php config:
+
+-   `/var/log/nginx/*err*log` NginX webserver error logs
+-   `/var/log/apache2/*err*log` Apache webserver & mod_php error logs
+-   `/var/log/*php*log` PHP-FPM & PHP-CGI error logs
+-   `/var/log/php5/*log` PHP-FPM & PHP-CGI error logs
+-   `/path/to/site/err*log` Some hosting environments
+
+And a **CiviCRM specific debug log** file - location varies depending on hosting environment *and* CMS, refer to [this wiki page](https://wiki.civicrm.org/confluence/display/CRMDOC/Debugging+for+developers#Debuggingfordevelopers-Logfiles) for location -
+
+    path/to/site/path/to/civicrm/files/ConfigAndLog/CiviCRM*log
+
+*(The `*`s above represent a wildcard, not an actual filename. Eg the last entry might be public_html/error_log on Bluehost.)*
+
+!!! Tip
+    Buildkit uses [amp](https://github.com/amp-cli/amp) to install sites which means you have to look in `~/.amp`. Log files are in `~/.amp/log` and separated by site.
+
+Once you've located these files, you can download them to view, or you can use tools like `tail` or `less +F` to follow the files. I prefer to follow logfiles because you can watch the error appear each time.
+
+**Displaying errors to screen**
+
+You may prefer to display errors to screen. This is probably disabled on your site because it's a security risk to some degree - an attacker can see more information when errors are visible, so the default configuration is often to restrict visibility to people with server access (via the logfiles above).
+
+To enable error display, either:
+
+**Configure your PHP to display errors for your site** via `php.ini` / `.htaccess` (see [How can I get PHP errors to display](http://stackoverflow.com/questions/1053424/how-do-i-get-php-errors-to-display)), OR 
+
+**Add PHP code to enable error display** (you can add it in `civicrm.settings.php` or the top of the `index.php` of your host CMS).
+
+    <?php
+      error_reporting(E_ALL);
+      ini_set('display_errors', TRUE);
+      ini_set('display_startup_errors', TRUE);
+
+**Making sense of what you see**
+
+Once you've taken one of the above approaches, try reproducing the actions which led to a white screen. If all's gone well, you should see an error now (on screen, or in your terminal / SSH session).
+
+This is where you can start debugging meaningfully. There's a good chance you're exhausting server resources (timeouts, memory exhaustion) or hitting some coding error, but once you have the relevant error message at hand you'll be much better equipped to track down the source of the problem affecting your site.
+
+**Further reading**
+
+ * [Stack Overflow: How do I get PHP errors to display?](http://stackoverflow.com/questions/1053424/how-do-i-get-php-errors-to-display)
+ * https://civicrm.stackexchange.com/questions/376/where-should-one-look-for-logs-when-debugging-a-new-problem
+ * [Drupal.org: Blank pages or White Screen of Death](https://www.drupal.org/node/158043)
+ * [Joomla SE: What is an efficient way to troubleshoot a White Screen of Death](https://joomla.stackexchange.com/questions/299/what-is-an-efficient-way-to-troubleshoot-a-white-screen-of-death)
+ * [CiviCRM wiki: Debugging for developers](https://wiki.civicrm.org/confluence/display/CRMDOC/Debugging+for+developers#Debuggingfordevelopers-Logfiles)
+
+**Notes**
+
+If this is the first time you've looked, there may be other errors visible which don't relate to the problem at hand. You may still need to discern what the actual problem is ...
+
+If you're not familiar with UNIX, this may seem like a lot of effort. It's a lot less effort than *guessing* your way through a problem though!
+
+
+## Credits
+
+Some content from this page was migrated from other sources
+and contributed by the following authors: 
+
+* Chris Burgess
+* Sean Madsen
diff --git a/docs/documentation.md b/docs/documentation.md
index c02d5e9c66d6717d99a4f36dc1f86630c74356fb..3bc28cb96ac709c4e7fb4a715e317d5a918f4c42 100644
--- a/docs/documentation.md
+++ b/docs/documentation.md
@@ -2,7 +2,7 @@
 
 To *read* documentation, go to [docs.civicrm.org](https://docs.civicrm.org) for the most high-level list of all active documentation.
 
-This page describes the documentation systems within CiviCRM and how to contribute.
+This page describes the details of the documentation systems within CiviCRM and how to contribute. We also have a more [basic overview] (https://docs.civicrm.org/user/en/latest/the-civicrm-community/contributing-to-this-manual/) on how to contribute to this guide or the user guide. 
 
 !!! note "Note: the wiki is not covered here"
     The [wiki] has historically been CiviCRM's documentation system but is currently being phased out. As of early 2017, documentation is still somewhat split between the wiki the the guide books described below, but we are working to eventually consolidate *all* documentation into guide books. A [migration process][migration] is currently underway for this Developer Guide, and a process will [likely](https://github.com/civicrm/civicrm-docs/issues/17) follow for a dedicated Administrator Guide, as well as [extension guides](https://github.com/civicrm/civicrm-docs/issues/14).
@@ -16,9 +16,6 @@ This page describes the documentation systems within CiviCRM and how to contribu
 
 We are using [MkDocs](http://www.mkdocs.org) to produce books. The content for each of these books is written in [markdown](/markdownrules.md), stored in text files, and hosted in a repository on GitHub. Then, the books are automatically published to [docs.civicrm.org](https://docs.civicrm.org) using our custom [publishing system](https://github.com/civicrm/civicrm-docs).
 
-### Languages
-
-A book can have multiple languages, and we use separate repositories for different languages.
 
 ### Versions
 
@@ -30,6 +27,9 @@ If you're improving current documentation, please edit the `master` branch, whic
 
 In rarer cases, if you have an edit that pertains to a specific version, (e.g. documentation about a feature in an *older* version of CiviCRM, which does not require documentation in the latest version), then please edit the branch corresponding to that version.
 
+### Languages
+
+A book can have multiple languages, and we use separate repositories for different languages. For example, you can click *See all X editions* and find the repositories for additional languages.  
 
 ## Contributing to documentation
 
@@ -48,21 +48,13 @@ Before diving into editing, you may find helpful information within the followin
 
 ### Submitting issues
 
-The simplest way to help out is to *describe* a change that you think *should* be made by writing a new issue in the issue queue for the book you are reading. Then someone will see your issue and act on it, hopefully fast. Each book has its own issue queue. First find the GitHub repository for the book (listed in the above table), then when viewing on GitHub, click on "Issues". You will need an account on GitHub to submit a new issue, but creating one is quick and free.
+The simplest way to help out is to *describe* a change that you think *should* be made by writing a new issue in the issue queue for the GitHub book you are reading. Then someone will see your issue and act on it, hopefully fast. Each book has its own issue queue. First find the GitHub repository for the book (listed in the above table), then when viewing on GitHub, click on "Issues". You will need an account on GitHub to submit a new issue, but creating one is quick and free.
 
 ### Editing through GitHub
 
-Suggest specific changes by making the changes within the text editor on GitHub. (You will first need an account on GitHub.)
+Please see the documentation for editing with Git in the [CiviCRM user guide](https://docs.civicrm.org/user/en/stable/the-civicrm-community/contributing-to-this-manual/#single_changes).   
 
-1. Find the page in the book you wish to edit.
-1. Click on the pencil icon at the top right.
-1. Make changes within the editor on GitHub.
-1. Click "Propose file change" at the bottom.
-1. **Important**: Click "Create pull request" and confirm. (You're not done until you create a pull request.)
-
-After you follow the steps above, someone else will review your changes and hopefully accept them, at which point you'll be notified via email.
-
-### Editing locally with MkDocs {:#mkdocs}
+### Testing locally with MkDocs {:#mkdocs}
 
 The most advanced way to work on a book is to use git to download all the markdown files to your computer, edit them locally, preview the changes with [MkDocs](http://mkdocs.org/), then use git to push those changes to your personal fork, and finally make a "pull request" on the main repository. This approach makes editing very fast and easy, but does require a bit of setup, and some knowledge of how git works.
 
@@ -78,10 +70,10 @@ The most advanced way to work on a book is to use git to download all the markdo
     ```
 
 1.  Obtain the source files for the book you want to edit
-    1.  Find the repository on GitHub &mdash; *Most books will have a link to this repo at the top right of every page (with a GitHub icon)*
+    1.  Find the repository on GitHub *(see "repository" links above, or the "GitHub" link on the bottom left of screen of the documentation you are reading)*
     1.  Fork the repository on GitHub.
     1.  Clone *your fork* of the repository to your computer
-
+				
         ```bash
         git clone https://github.com/YourGitHubUserName/civicrm-dev-docs.git
         cd civicrm-dev-docs
diff --git a/docs/extensions/civix.md b/docs/extensions/civix.md
index 45e62f977ccc0446d72fa65dccdb060a3156dfb4..5c104670cdf398d341a6acaa82ed237a882f0236 100644
--- a/docs/extensions/civix.md
+++ b/docs/extensions/civix.md
@@ -624,8 +624,7 @@ This creates one file:
     that the parameters and return values must be processed in a
     particular way (as demonstrated by the auto-generated file).
 
-<!-- fixme - clarify is this 4.3 and later? -->
-For use with CiviCRM 4.3, you can also add the `–schedule` option (e.g.
+For use with CiviCRM 4.3 and later, you can also add the `–schedule` option (e.g.
 `–schedule Hourly`). This will create another file:
 
 -   `api/v3/NewEntity/NewAction.mgd.php` provides the scheduling
diff --git a/docs/hooks/hook_civicrm_alterAPIPermissions.md b/docs/hooks/hook_civicrm_alterAPIPermissions.md
index 6df2ac9ca919332f0a3d5dfebe0cbf746a44e502..c8b2ea9d5fd4e4fafe1f8a44bc314ad11cc52285 100644
--- a/docs/hooks/hook_civicrm_alterAPIPermissions.md
+++ b/docs/hooks/hook_civicrm_alterAPIPermissions.md
@@ -9,7 +9,7 @@ unconditionally).
 
 !!! Note
     If a given entity/action permissions are unset, the default
-    "access CiviCRM" permission is enforced.
+    "administer CiviCRM" permission is enforced.
 
 
 ## Definition
@@ -39,6 +39,10 @@ hook_civicrm_alterAPIPermissions($entity, $action, &$params, &$permissions)
 
 ## Example
 
+The `alterAPIPermissions` function is prefixed with the full extension name, all lowercase,
+followed by `_civicrm_alterAPIPermissions`. For an extension "CiviTest" the hook
+would be placed in the `civitest.php` file and might look like:
+
 ```php
 function civitest_civicrm_alterAPIPermissions($entity, $action, &$params, &$permissions)
 {
@@ -61,9 +65,9 @@ function civitest_civicrm_alterAPIPermissions($entity, $action, &$params, &$perm
 }
 ```
 
-When developing an extension with custom API, this code is placed
-directly in the API php file that you have created. In this case the
-extension would be named CiviTest. The API function for the GET would be
-`function civicrm_api3_civi_test_get();`. The `alterAPIPermissions`
-function is prefixed with the full extension name, all lowercase,
-followed by `_civicrm_alterAPIPermissions`.
+The API function for the "get" action for the new custom API entity called "Foo" would be
+`function civicrm_api3_foo_get();`.
+
+
+
+
diff --git a/docs/hooks/hook_civicrm_buildProfile.md b/docs/hooks/hook_civicrm_buildProfile.md
index 1899d8e10835961f4054da9806fb9f61ef2bc0c4..90a5dab5c4b50d5c1f6532ca8458b30b8e0cd0b0 100644
--- a/docs/hooks/hook_civicrm_buildProfile.md
+++ b/docs/hooks/hook_civicrm_buildProfile.md
@@ -6,11 +6,11 @@ This hook is called while preparing a profile form.
 
 ## Definition
 
-    buildProfile($name)
+    buildProfile($profileName)
 
 ## Parameters
 
--   $name - the (machine readable) name of the profile.
+-   $profileName - the (machine readable) name of the profile.
 
 ## Returns
 
@@ -21,9 +21,9 @@ immediately obvious how I can use this. I could do something like this:
 
 
 
-    function myext_civicrm_buildProfile($name) {
+    function myext_civicrm_buildProfile($profileName) {
 
-      if ($name === 'MyTargetedProfile) {
+      if ($profileName === 'MyTargetedProfile) {
 
         CRM_Core_Resources::singleton()->addScriptFile('org.example.myext', 'some/fancy.js', 100);
 
diff --git a/docs/hooks/hook_civicrm_contactListQuery.md b/docs/hooks/hook_civicrm_contactListQuery.md
index 50dc83159aaecc07aa9e5b10d6dad0a84e4667bf..d2e9663fe334201cffb1dd6324c1035325e8f801 100644
--- a/docs/hooks/hook_civicrm_contactListQuery.md
+++ b/docs/hooks/hook_civicrm_contactListQuery.md
@@ -18,7 +18,7 @@ the list of contacts to display.
 
 ## Definition
 
-    hook_civicrm_contactListQuery( &$query, $name, $context, $id )
+    hook_civicrm_contactListQuery( &$query, $queryText, $context, $id )
 
 ## Parameters
 
@@ -29,7 +29,7 @@ the list of contacts to display.
     -   the contact 'data' to display in the autocomplete dropdown
         (usually contact.sort_name - aliased as 'data')
     -   the contact IDs
--   $name - the name string to execute the query against (this is the
+-   $queryText - the name string to execute the query against (this is the
     value being typed in by the user)
 -   $context - the context in which this ajax call is being made (for
     example: 'customfield', 'caseview')
@@ -56,7 +56,7 @@ This example limits contacts in my contact reference field lookup
             $query = "
     SELECT c.sort_name as data, c.id
     FROM civicrm_contact c, civicrm_group_contact cg
-    WHERE c.sort_name LIKE '$name%'
+    WHERE c.sort_name LIKE '$queryText%'
     AND   cg.group_id IN ( 5 )
     AND   cg.contact_id = c.id
     AND   cg.status = 'Added'
diff --git a/docs/hooks/hook_civicrm_optionValues.md b/docs/hooks/hook_civicrm_optionValues.md
index 8078fc804110c014a0a07f01db757135755be484..fcc0f5dc6234f291cac768dd140c6c6f696319c9 100644
--- a/docs/hooks/hook_civicrm_optionValues.md
+++ b/docs/hooks/hook_civicrm_optionValues.md
@@ -12,13 +12,13 @@ hook to add/remove options from the option group.
 ## Definition
 
 ```php
-hook_civicrm_optionValues(&$options, $name)
+hook_civicrm_optionValues(&$options, $groupName)
 ```
 
 ## Parameters
 
 -   array `$options` - the current set of options
--   string `$name` - the name of the option group
+-   string `$groupName` - the name of the option group
 
 ## Returns
 
diff --git a/docs/hooks/hook_civicrm_processProfile.md b/docs/hooks/hook_civicrm_processProfile.md
index 05888e9afa48981d6ea899b250a25760e872f8f6..3b6401549423edf295b0644e48e743d33b18b845 100644
--- a/docs/hooks/hook_civicrm_processProfile.md
+++ b/docs/hooks/hook_civicrm_processProfile.md
@@ -6,11 +6,11 @@ This hook is called processing a valid profile form submission (e.g. for "civicr
 
 ## Definition
 
-    processProfile($name)
+    processProfile($profileName)
 
 ## Parameters
 
--   $name - the (machine readable) name of the profile.
+-   $profileName - the (machine readable) name of the profile.
 
 !!! Tip
     In SQL, this corresponds to the "name" column of table "civicrm_uf_group"
diff --git a/docs/hooks/hook_civicrm_searchProfile.md b/docs/hooks/hook_civicrm_searchProfile.md
index d8216db6f22014845f56c88b2178c74e525594a8..f79739384439cdd4c6ada631f4ef8a4a8da8b342 100644
--- a/docs/hooks/hook_civicrm_searchProfile.md
+++ b/docs/hooks/hook_civicrm_searchProfile.md
@@ -7,11 +7,11 @@ profile).
 
 ## Definition
 
-    searchProfile($name)
+    searchProfile($profileName)
 
 ## Parameters
 
--   $name - the (machine readable) name of the profile.
+-   $profileName - the (machine readable) name of the profile.
 
 ## Returns
 
diff --git a/docs/hooks/hook_civicrm_validateProfile.md b/docs/hooks/hook_civicrm_validateProfile.md
index b961c48a575d7b156b34a59097f78f7ec36099b4..fbba815fa5899051467fd73f91096d2142a7b60f 100644
--- a/docs/hooks/hook_civicrm_validateProfile.md
+++ b/docs/hooks/hook_civicrm_validateProfile.md
@@ -6,11 +6,11 @@ This hook is called while validating a profile form submission.
 
 ## Definition
 
-    validateProfile($name)
+    validateProfile($profileName)
 
 ## Parameters
 
--   $name - the (machine readable) name of the profile.
+-   $profileName - the (machine readable) name of the profile.
 
 ## Returns
 
diff --git a/docs/hooks/hook_civicrm_viewProfile.md b/docs/hooks/hook_civicrm_viewProfile.md
index 3e3636f7b1eb0d5ff76405fd58634e59d5687a10..40bfda32d846376436d4191bcc7afaa0a28cbbb8 100644
--- a/docs/hooks/hook_civicrm_viewProfile.md
+++ b/docs/hooks/hook_civicrm_viewProfile.md
@@ -6,11 +6,11 @@ This hook is called while preparing a read-only profile screen.
 
 ## Definition
 
-    viewProfile($name)
+    viewProfile($profileName)
 
 ## Parameters
 
--   $name - the (machine readable) name of the profile.
+-   $profileName - the (machine readable) name of the profile.
 
 ## Returns
 
diff --git a/docs/hooks/setup/symfony.md b/docs/hooks/setup/symfony.md
new file mode 100644
index 0000000000000000000000000000000000000000..1345298fbabefa64e3f764d4738c50d2a57118d1
--- /dev/null
+++ b/docs/hooks/setup/symfony.md
@@ -0,0 +1,133 @@
+## Overview
+
+The [__Symfony EventDispatcher__](http://symfony.com/components/EventDispatcher) is an
+event library used by several PHP applications and frameworks.  For example,
+Symfony SE, Drupal 8, Magento, Laravel, CiviCRM, and many others support
+`EventDispatcher`.  It provides a common mechanism for dispatching and listening
+to events.
+
+In CiviCRM v4.7.19+, you can use Symfony `EventDispatcher` with hooks.
+
+```php
+Civi::dispatcher()->addListener('hook_civicrm_EXAMPLE', $callback, $priority);
+```
+
+Using `EventDispatcher` is useful if you need more advanced features, such as:
+
+ * Setting the priority of an event-listener
+ * Registering an object-oriented event-listener
+ * Registering a dynamic, on-the-fly event-listener
+ * Registering multiple listeners for the same event
+ * Registering for internal/unpublished events
+
+For a general introduction or background on `EventDispatcher`, consult the [Symfony documentation](http://symfony.com/doc/2.7/components/event_dispatcher.html).
+
+## Example: `Civi::dispatcher()`
+
+In this case, we have a CiviCRM extension or Drupal module named `example`.
+During the system initialization, we lookup the `EventDispatcher`, call
+`addListener()`, and listen for `hook_civicrm_alterContent`.
+
+```php
+function example_civicrm_config(&$config) {
+  if (isset(Civi::$statics[__FUNCTION__])) { return; }
+  Civi::$statics[__FUNCTION__] = 1;
+
+  Civi::dispatcher()->addListener('hook_civicrm_alterContent', '_example_say_hello');
+}
+
+function _example_say_hello($event) {
+  $event->content = 'hello ' . $event->content;
+}
+```
+
+!!! tip "Using the `$event` object"
+    Hook parameters are passed as an object, `$event`.
+    For example, [`hook_civicrm_alterContent`](/hooks/hook_civicrm_alterContent/)
+    has the parameters `(&$content, $context, $tplName, &$object)`.
+    You can access the data as `$event->content`, `$event->context`, `$event->tplName`, and `$event->object`.
+
+!!! tip "Using `hook_civicrm_config`"
+    In some environments, `hook_civicrm_config` runs multiple times. The flag
+    `Civi::$statics[__FUNCTION__]` prevents duplicate listeners.
+
+## Example: `Container::findDefinition()`
+
+In this case, we have a CiviCRM extension or Drupal module named `example`.
+We lookup the defintion of the `dispatcher` service and amend it.
+
+```php
+function example_civicrm_container($container) {
+  $container->findDefinition('dispatcher')
+    ->addMethodCall('addListener', array('hook_civicrm_alterContent', '_example_say_hello'));
+}
+
+function _example_say_hello($event) {
+  $event->content = 'hello ' . $event->content;
+}
+```
+
+<!--
+  TODO: an example using a container-service and tag.  See "Registering Event Listeners
+  in the Service Container" from http://symfony.com/doc/2.7/components/event_dispatcher.html
+-->
+
+## Events
+
+CiviCRM broadcasts many different events through the `EventDispatcher`. These
+events fall into two categories:
+
+ * __External Events/Hooks__ (v4.7.19+): These have a prefix `hook_civicrm_*`. They extend
+   the class [`GenericHookEvent`](https://github.com/civicrm/civicrm-core/blob/master/Civi/Core/Event/GenericHookEvent.php)
+   (which, in turn, extends  [`Event`](http://api.symfony.com/2.7/Symfony/Component/EventDispatcher/Event.html)).
+   Hooks are simulcast across `EventDispatcher` as well as CMS-specific event systems.
+ * __Internal Events__ (v4.5.0+): These have a prefix `civi.*`. They extend
+   the class  [`Event`](http://api.symfony.com/2.7/Symfony/Component/EventDispatcher/Event.html).
+   They are *only* broadcast via `EventDispatcher` (**not** CMS-specific event systems).
+
+You can recognize these events by their naming convention. Compare:
+
+```php
+// Listen to a hook. Note the prefix, "hook_civicrm_*".
+Civi::dispatcher()->addListener('hook_civicrm_alterContent', $callback, $priority);
+
+// Listen to an internal event. Note the prefix, "civi.*".
+Civi::dispatcher()->addListener('civi.api.resolve', $callback, $priority);
+```
+
+## Methods
+
+The `EventDispatcher` has several different methods for registering a
+listener.  Our examples have focused on the simplest one, `addListener()`,
+but the Symfony documentation describes other methods (`addSubscriber()`,
+`addListenerService()`, and `addSubscriberService()`).  See also:
+
+ * [Symfony EventDispatcher](http://symfony.com/doc/2.7/components/event_dispatcher.html)
+ * [Symfony ContainerAwareEventDispatcher](http://symfony.com/doc/2.7/components/event_dispatcher/container_aware_dispatcher.html)
+
+!!! tip "Using `addListener()`"
+    When calling `addListener()`, you _can_ pass any [PHP callable](http://php.net/manual/en/language.types.callable.php).
+    However, _in practice_, the safest bet is to pass a string (function-name) or array
+    (class-name, function-name). Other formats may not work with the
+    [container-cache](http://symfony.com/doc/2.7/components/dependency_injection/compilation.html).
+
+## History
+
+ * _CiviCRM v4.5.0_: Introduced Symfony EventDispatcher for internal use (within `civicrm-core`). For example,
+   APIv3 dispatches the events `civi.api.resolve` and `civi.api.authorize` while executing an API call.
+ * _CiviCRM v4.7.0_: Introduced `hook_civicrm_container`.
+ * _CiviCRM v4.7.0_: Integrated the Symfony `Container` and `EventDispatcher`.
+ * _CiviCRM v4.7.19_: Integrated `hook_civicrm_*` with the Symfony `EventDispatcher`.
+ * _CiviCRM v4.7.19_: Added the `Civi::dispatcher()` function.
+
+## Limitations
+
+ * _Boot-critical hooks_: `hook_civicrm_config`, `hook_civicrm_container`, and `hook_civicrm_entityTypes`
+   are fired during the bootstrap process -- before the Symfony subsystems are fully online. Consequently,
+   you may not be able to listen for these hooks.
+ * _Opaque CMS listeners_: Most hooks are dispatched through `EventDispatcher` as well as the traditional
+   hook systems for Drupal modules, Joomla plugins, WordPress plugins, and/or CiviCRM extensions.
+   This is accomplished by _daisy-chaining_: first, the event is dispatched with `EventDispatcher`; then, the
+   listener `CiviEventDispatcher::delegateToUF()` passes the event down to the other systems.
+   If you inspect `EventDispatcher`, there will be one listener (`delegateToUF()`)
+   which represents _all_ CMS-based listeners.
diff --git a/mkdocs.yml b/mkdocs.yml
index e75e479c6810952bc78f56859bd796ab38276668..1574a42ddf16d2b760095b0735b89e4086b1ce40 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -86,6 +86,7 @@ pages:
 - Hooks:
   - Using hooks: hooks.md                        # page-tree = NEED_PAGE_MOVE to /hooks/usage.md
   - Setup:
+    - Hooks with Symfony: hooks/setup/symfony.md
     - Hooks with Joomla: hooks/setup/joomla.md
     - Hooks with Drupal: hooks/setup/drupal.md
     - Hooks with Wordpress: hooks/setup/wordpress.md