GuzzleMiddleware::url() - Add helper to quickly compose Civi-related URLs
authorTim Otten <totten@civicrm.org>
Tue, 9 Feb 2021 05:03:55 +0000 (21:03 -0800)
committerTim Otten <totten@civicrm.org>
Fri, 12 Feb 2021 20:53:23 +0000 (12:53 -0800)
CRM/Utils/GuzzleMiddleware.php [new file with mode: 0644]

diff --git a/CRM/Utils/GuzzleMiddleware.php b/CRM/Utils/GuzzleMiddleware.php
new file mode 100644 (file)
index 0000000..2b8c4d5
--- /dev/null
@@ -0,0 +1,109 @@
+<?php
+
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Additional helpers/utilities for use as Guzzle middleware.
+ */
+class CRM_Utils_GuzzleMiddleware {
+
+  /**
+   * Add this as a Guzzle handler/middleware if you wish to simplify
+   * the construction of Civi-related URLs. It enables URL schemes for:
+   *
+   * - route://ROUTE_NAME (aka) route:ROUTE_NAME
+   * - var://PATH_EXPRESSION (aka) var:PATH_EXPRESSION
+   * - ext://EXTENSION/FILE (aka) ext:EXTENSION/FILE
+   * - assetBuilder://ASSET_NAME?PARAMS (aka) assetBuilder:ASSET_NAME?PARAMS
+   *
+   * Compare:
+   *
+   * $http->get(CRM_Utils_System::url('civicrm/dashboard', NULL, TRUE, NULL, FALSE))
+   * $http->get('route:civicrm/dashboard')
+   *
+   * $http->get(Civi::paths()->getUrl('[civicrm.files]/foo.txt'))
+   * $http->get('var:[civicrm.files]/foo.txt')
+   *
+   * $http->get(Civi::resources()->getUrl('my.other.ext', 'foo.js'))
+   * $http->get('ext:my.other.ext/foo.js')
+   *
+   * $http->get(Civi::service('asset_builder')->getUrl('my-asset.css', ['a'=>1, 'b'=>2]))
+   * $http->get('assetBuilder:my-asset.css?a=1&b=2')
+   *
+   * Note: To further simplify URL expressions, Guzzle allows you to set a 'base_uri'
+   * option (which is applied as a prefix to any relative URLs). Consider using
+   * `base_uri=auto:`. This allows you to implicitly use the most common types
+   * (routes+variables):
+   *
+   * $http->get('civicrm/dashboard')
+   * $http->get('[civicrm.files]/foo.txt')
+   *
+   * @return \Closure
+   */
+  public static function url() {
+    return function(callable $handler) {
+      return function (RequestInterface $request, array $options) use ($handler) {
+        $newUri = self::filterUri($request->getUri());
+        if ($newUri !== NULL) {
+          $request = $request->withUri(\CRM_Utils_Url::parseUrl($newUri));
+        }
+
+        return $handler($request, $options);
+      };
+    };
+  }
+
+  /**
+   * @param \Psr\Http\Message\UriInterface $oldUri
+   *
+   * @return string|null
+   *   The string formation of the new URL, or NULL for unchanged URLs.
+   */
+  protected static function filterUri(\Psr\Http\Message\UriInterface $oldUri) {
+    // Copy the old ?query-params and #fragment-params on top of $newBase.
+    $copyParams = function ($newBase) use ($oldUri) {
+      if ($oldUri->getQuery()) {
+        $newBase .= strpos($newBase, '?') !== FALSE ? '&' : '?';
+        $newBase .= $oldUri->getQuery();
+      }
+      if ($oldUri->getFragment()) {
+        $newBase .= '#' . $oldUri->getFragment();
+      }
+      return $newBase;
+    };
+
+    $hostPath = urldecode($oldUri->getHost() . $oldUri->getPath());
+    $scheme = $oldUri->getScheme();
+    if ($scheme === 'auto') {
+      // Ex: 'auto:civicrm/my-page' ==> Router
+      // Ex: 'auto:[civicrm.root]/js/foo.js' ==> Resource file
+      $scheme = ($hostPath[0] === '[') ? 'var' : 'route';
+    }
+
+    switch ($scheme) {
+      case 'assetBuilder':
+        // Ex: 'assetBuilder:dynamic.css' or 'assetBuilder://dynamic.css?foo=bar'
+        // Note: It's more useful to pass params to the asset-builder than to the final HTTP request.
+        $assetParams = [];
+        parse_str('' . $oldUri->getQuery(), $assetParams);
+        return \Civi::service('asset_builder')->getUrl($hostPath, $assetParams);
+
+      case 'ext':
+        // Ex: 'ext:other.ext.name/file.js' or 'ext://other.ext.name/file.js'
+        [$ext, $file] = explode('/', $hostPath, 2);
+        return $copyParams(\Civi::resources()->getUrl($ext, $file));
+
+      case 'var':
+        // Ex: 'var:[civicrm.files]/foo.txt' or  'var://[civicrm.files]/foo.txt'
+        return $copyParams(\Civi::paths()->getUrl($hostPath, 'absolute'));
+
+      case 'route':
+        // Ex: 'route:civicrm/my-page' or 'route://civicrm/my-page'
+        return $copyParams(\CRM_Utils_System::url($hostPath, NULL, TRUE, NULL, FALSE));
+
+      default:
+        return NULL;
+    }
+  }
+
+}