You are here

function gardens_site_data_refresh_domains in Acquia Cloud Site Factory Connector 8.2

Same name and namespace in other branches
  1. 8 acsf_init/lib/sites/g/sites.inc \gardens_site_data_refresh_domains()

Returns data for the specified domains directly from the JSON file.

Optionally also stores the data in APC.

Parameters

array $domains: The domain names to look up in the JSON file.

Return value

array An array keyed by the specified domains, whose values are site data arrays or 0 if no site was found for the given domain. If a domain is not present in the array keys, this indicates a sites.json read failure.

2 calls to gardens_site_data_refresh_domains()
apc_rebuild.php in acsf_init/lib/sites/g/apc_rebuild.php
gardens_site_data_refresh_one in acsf_init/lib/sites/g/sites.inc
Returns data for a single domain.

File

acsf_init/lib/sites/g/sites.inc, line 387
ACSF helper functions for Drupal's multi-site directory aliasing feature.

Code

function gardens_site_data_refresh_domains(array $domains) {
  $location = gardens_site_data_get_filepath();
  $data = [];
  foreach ($domains as $domain) {
    $domain = trim($domain);

    // Below code expects the JSON file to contain newlines such that
    // - all data except the 'sites' data and the closing brace are on the first
    //   line;
    // - all data for a key/value pair representing one single site, on a single
    //   line. (See below for example.)
    // This way we can isolate data for one site by performing a grep command,
    // which is much quicker than reading all data into one JSON object. The
    // code is built to keep working if the formatting changes by accident; it
    // will just be much slower. Also, our grep command does not assume that the
    // key for a site is included in double quotes (apparently for fear of
    // having a file in illegal JSON format, which does not double-quote its
    // object keys...) so we may hit false positives.
    // Acquia rules disallow exec() with dynamic arguments.
    // phpcs:disable
    exec(sprintf("grep %s %s --no-filename --color=never --context=0", escapeshellarg($domain), escapeshellarg($location)), $output_array, $exit_code);

    // phpcs:enable
    $result = trim(implode("\n", $output_array));
    if (empty($result)) {

      // Log an explicit fail in APC if we cannot find the domain, so that we
      // can take advantage of APC caching the "fail" also. Differentiate
      // between values for "site not found" and "read failure" so future
      // requests can emit different responses for them. (From the docs about
      // Gnu grep: exit status is 0 if a line is selected -which should never
      // happen here-, 1 if no line is selected, 2 if error encountered.)
      if ($exit_code === 1) {
        $data[$domain] = 0;
      }
    }
    else {

      // $result is in the form of
      // "example.com": {"name": "g123", "flags": {}},
      // (with or without the trailing comma).  Since we didn't include quotes,
      // we may have more than 1 line returned from the grep command, typically
      // if the searched-for site domain is a substring of another site domain.
      // The "m" (multiline) modifier is used in the regular expression so that
      // the begin and end anchors can match the beginning and end of any one of
      // those lines, rather than having to match the entire string from
      // beginning to end (which fails if there is more than 1 line of results).
      $matches = [];
      $pattern = '@^\\s*"' . preg_quote($domain, '@') . '": ({.+}),?$@m';
      if (preg_match($pattern, $result, $matches)) {
        $found_site = json_decode($matches[1], TRUE);
      }

      // Retrieve the first line of the JSON file, which contains the global
      // site settings data.
      $f = fopen($location, 'r');
      $json = fgets($f);
      fclose($f);
      $json = rtrim($json, ",\n");
      $json .= "}";
      $global_map_data = json_decode($json, TRUE);
      if (empty($found_site) || empty($global_map_data)) {

        // This will happen if the domain appears in the JSON file, but the
        // format of the file has changed such that the grep-based single-line
        // parsing no longer works.
        if (class_exists('Drupal') && \Drupal::hasService('logger.factory')) {
          \Drupal::logger('acsf')
            ->alert('Unable to extract site data for site @site from sites.json line "@line".', [
            '@site' => $domain,
            '@line' => $result,
          ]);
        }
        elseif (function_exists('syslog')) {
          syslog(LOG_ERR, sprintf('Unable to extract site data for site %s from sites.json line "%s".', $domain, $result));
        }
        if ($map = gardens_site_data_load_file()) {
          if (!empty($map['sites'][$domain])) {
            $data[$domain] = gardens_site_data_build_data($map['sites'][$domain], $map);
          }
          else {

            // The domain isn't actually present; apparently $domain is a
            // substring of the domain(s) matched by grep. (Or, who knows: the
            // string might appear somewhere else on the line than the 'key'.)
            $data[$domain] = 0;
          }
        }

        // If $data[$domain] was not set here, the file is readable (or there's
        // a race condition and the error just appeared) because we did get a
        // line of data returned earlier. So the JSON is invalid.
      }
      else {
        $data[$domain] = gardens_site_data_build_data($found_site, $global_map_data);
      }
    }
    if (isset($data[$domain])) {

      // Update the current record in place *if* we are using APC.
      if (GARDENS_SITE_DATA_USE_APC) {
        gardens_site_data_cache_set($domain, $data[$domain]);
      }
    }
    else {

      // Report the read failure, only if Drupal is bootstrapped.
      if (function_exists('drupal_register_shutdown_function')) {

        // Since reporting involves contacting the Site Factory it should be
        // done in a way that does not affect pageload.
        drupal_register_shutdown_function('gardens_site_data_json_alert_flag_set');
      }

      // Stop processing further domains.
      break;
    }
  }
  if (count($data) == count($domains)) {

    // No read failure encountered; all domains were accounted for / cached.
    if (gardens_site_data_json_alert_flag_check() && function_exists('drupal_register_shutdown_function')) {

      // Clear the flag. Since it involves contacting the Site Factory it should
      // be done in a way that does not affect pageload.
      drupal_register_shutdown_function('gardens_site_data_json_alert_flag_clear');
    }
  }
  else {

    // If we were checking several domains and any check reported a read failure
    // then don't try reading the file again for other domains; cache the failed
    // domain plus any that were not processed yet, as "read failure". (It's
    // unlikely that we gathered data for some domains before encountering a
    // read failure for another one, but account for it.)
    if (GARDENS_SITE_DATA_USE_APC) {
      foreach ($domains as $domain) {
        if (!isset($data[$domain])) {
          gardens_site_data_cache_set($domain, NULL);
        }
      }
    }
  }
  return $data;
}