loopit-8.x-1.x-dev/src/Aggregate/AggregateService.php

src/Aggregate/AggregateService.php
<?php

namespace Drupal\loopit\Aggregate;

use Drupal\Component\Utility\NestedArray;

/**
 * @todo comments
 */
class AggregateService extends AggregateArray {

  /**
   * Unserialize not instanciated services.
   *
   * @param AggregateArray $aggregate
   * @param mixed $current
   * @param string $index
   * @return mixed
   */
  public static function onCurrentServiceUnserialize(AggregateArray $aggregate, $current, $index) {
    if ($current && $aggregate->getArrayParents() === ['services']) {
      $unserialize = unserialize($current);
      $current = ['__CLASS__'=> $unserialize['class']];
    }

    return $current;
  }

  public static function getClasses($leaf_match) {

    if (!isset($leaf_match)) {
      $leaf_match = ['*class' => '*', '*Class' => '*'];
    }

    AggregateFilter::$context['system.module.files'] = \Drupal::state()->get('system.module.files');

    $options = [];
    $options['onCurrent'][] = AggregateFilter::class . '::onCurrentSubsetArrayParents';
    $options['subset_array_parents'] = [
      '\*services' => $leaf_match,
      'services' => $leaf_match,
    ];;
    $aggreg = self::createInstance(static::getClassesInfo(), $options);
    $aggreg->setIteratorClass('Drupal\loopit\Iterator\AggregateFilterIterator');
    // Init context variables
    $aggreg->context += array_fill_keys(['service_id_references', 'service_class_centric', 'service_only_references'], []);
    // onLeafClasses uses $ths->context so add this option once we have the instance
    $aggreg->options['onLeaf'][] = [$aggreg, 'onLeafClasses'];

    $iter = $aggreg->getIterator();
    // TODO: To test with ::CHILD_FIRST, ::LEAVES_ONLY
    foreach (new \RecursiveIteratorIterator($iter, \RecursiveIteratorIterator::SELF_FIRST) as $key => $value) {}
    ksort($aggreg->context['service_id_centric']);
    ksort($aggreg->context['service_id_references']);

    $aggreg->context['service_only_references'] = [];
    foreach ($aggreg->context['service_id_references'] as $service_id => & $service_classes) {
      // Replace intsance references by service id
      if (isset($service_classes['instance_references'])) {
        foreach ($service_classes['instance_references'] as $reference_name => $reference) {
          if (isset($aggreg->context['service_class_centric'][$reference]['id'])) {
            unset($service_classes['instance_references'][$reference_name]);
            $service_classes['service_references'][$reference_name] = $aggreg->context['service_class_centric'][$reference]['id'];
          }
        }
        // Drop if have become empty
        if (!$service_classes['instance_references']) {
          unset($service_classes['instance_references']);
        }
      }

      // Get referenced by
      if (isset($service_classes['class'])) {
        $class = $service_classes['class']; //AggregateFilter::$context['service_class_centric'][$service_id]['class'];
        if (isset($aggreg->context['service_class_centric'][$class]['id'])) {
          if (isset($aggreg->context['service_class_centric'][$class]['referenced_in_services'])) {
            $service_classes['referenced_in_services'] = $aggreg->context['service_class_centric'][$class]['referenced_in_services'];
          }
        }
      }
      // It means that it is filtered, so not show here
      else {
        $aggreg->context['service_only_references'][$service_id] = $service_classes;
        unset($aggreg->context['service_id_references'][$service_id]);
      }

    }

    return $aggreg;
  }

  public static function getClassesInfo() {

    $cid = 'loopit:aggregate:service_definitions';
    $cache = \Drupal::cache()->get($cid);
    if ($cache) {
      $aggregate_classes = $cache->data;
    }
    else {
      $aggregate_services = \Drupal::getContainer();
      $aggregate_services_casted = AggregateObject::castFast($aggregate_services);
      $aggregate_services_casted['services'] = array_diff_key($aggregate_services_casted['*serviceDefinitions'], $aggregate_services_casted['*services']);

      $options = [];
      $options['depth'] = 3;
      $options['onCurrent'][] = '::onCurrentServiceUnserialize';
      $options['onCurrent'][] = AggregateFilter::class . '::onCurrentSubsetArrayParents';
      $options['subset_array_parents'] = [
        '\*services' => NULL,
        'services' => NULL,
      ];
      $aggreg = self::createInstance($aggregate_services_casted, $options);
      $aggreg->setIteratorClass('Drupal\loopit\Iterator\AggregateFilterIterator');
      // Keep the root level of the input available for all levels
      $aggreg->context['aggregate_services_casted'] = $aggregate_services_casted;
      // Init empty aggregate object for cast, needed for ::onCurrentFromArrayParents
      $aggreg->context['aggregate_object_empty'] = AggregateObject::createInstance();
      $aggreg->options['onCurrent'][] = [$aggreg, 'onCurrentFromArrayParents'];

      $iter = $aggreg->getIterator();
      // TODO: To test with ::CHILD_FIRST, ::LEAVES_ONLY
      foreach (new \RecursiveIteratorIterator($iter, \RecursiveIteratorIterator::SELF_FIRST) as $key => $value) {}

      $aggregate_classes = $aggreg->getCacheNested();
      \Drupal::cache()->set($cid, $aggregate_classes);

    }

    return $aggregate_classes;
  }

  /**
   * Get the first level property values of services.
   *
   * @param AggregateArray $aggregate
   * @param mixed $current
   * @param string $index
   * @return mixed
   */
  public function onCurrentFromArrayParents($current, $index) {
    $aggregate = $this;

    // TODO: Move to options
    $service_errors = [];

    if ($current && is_array($current)) {
      $parents = $aggregate->getArrayParents();
      $options = $aggregate->getOptions();
      if ($parents === ['*services'] && isset($current[$options['array_parents_key']])) {
        // Get infos from "array_parents"
        $parents_to_get = $current[$options['array_parents_key']];
        $to_add = NestedArray::getValue($this->context['aggregate_services_casted'], $parents_to_get);
        foreach ($to_add as $key => $value) {
          // For array $value get only reserved keys (__CLASS__, ...)
          if (is_array($value)) {
            if (isset($value[$options['class_key']])) {
              $current[$key][$options['class_key']] = $value[$options['class_key']];
            }
            if (isset($value[$options['hash_key']])) {
              $current[$key][$options['hash_key']] = $value[$options['hash_key']];
            }
            if (isset($value[$options['array_parents_key']])) {
              $current[$key][$options['array_parents_key']] = $value[$options['array_parents_key']];
            }
          }
          else {
            $current[$key] = $value;
          }
        }
      }
      // For unserialized services cast the instance (only the 1st level)
      elseif ($parents === ['services'] && !in_array($index, $service_errors)) {
        $service = \Drupal::service($index);
        if (is_object($service)) {
          $to_add = $aggregate->context['aggregate_object_empty']->castObject($service);
          // Only class names.
          foreach ($to_add as $key => $value) {
            if (is_object($value)) {
              $current[$key] = ['__CLASS__' => get_class($value)];
            }
            // Cast to string just to have the property name
            else {
              $current[$key] = is_array($value) ? 'Array' : (string)$value;
            }
          }
        }
      }
    }

    return $current;
  }

  /**
   * Do on leaf transformation for service classes.
   *
   * @param Drupal\loopit\Aggregate\AggregateArray $aggregate
   * @param mixed $current
   * @param string $index
   * @return mixed
   */
  public function onLeafClasses($current, $index) {

    $aggregate = $this;

    $parents = $aggregate->getArrayParents();
    $parents[] = $index;

    if ($index == '__HASH__') {
      return $current;
    }
    if (in_array('__ARRAY_PARENTS__', $parents)) {
      return $current;
    }

    // The first parent is the "service" key: drop it.
    array_shift($parents);
    if (!$parents) {
      return $current;
    }

    // The next parent is the "service id".
    $service_id = array_shift($parents);
    // Concat remaining parents as a $service_property
    $service_property = array_reduce($parents, function($carry, $item) {
      return $carry . ($carry? '.' : '') . trim($item, '*');
    });

    // Some classes have leading "\"
    $current = ltrim($current, '\\');

    // Store by service id in two dimensions
    $this->context['service_id_centric'][$service_id][$service_property] = $current;

    // The service class
    if ($service_property == '__CLASS__') {

      // As "id" key for storing by class
      $this->context['service_class_centric'][$current]['id'] = $service_id;

      // As "class" key for storing by service id
      $this->context['service_id_references'][$service_id]['class'] = $current;
    }
    // Classes for other service properties.
    else {
      // Means that an object is referenced
      $ends_with = '.__CLASS__';
      $ends_with_count = strlen($ends_with);
      if(substr($service_property, -$ends_with_count) === $ends_with) {
        // It is an instance
        $in_services = 'referenced_in_services';
        $references_key = 'instance_references';
        $service_property = substr($service_property, 0, -$ends_with_count);
      }
      // Means that it is a class name, not object
      else {
        $in_services = 'classname_in_services';
        $references_key = 'classname_attributes';

      }

      // TODO: Some comments needed.
      $this->context['service_class_centric'][$current][$in_services][$service_id] = $service_property;
      $this->context['service_id_references'][$service_id][$references_key][$service_property] = $current;
    }

    return $current;
  }


}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc