You are here

class Generic in Drupal 7 to 8/9 Module Upgrader 8

Plugin annotation


@Rewriter(
 id = "_rewriter",
 deriver = "\Drupal\drupalmoduleupgrader\Plugin\DMU\Rewriter\GenericDeriver"
)

Hierarchy

Expanded class hierarchy of Generic

1 file declares its use of Generic
GenericTest.php in tests/src/Unit/Plugin/DMU/Rewriter/GenericTest.php

File

src/Plugin/DMU/Rewriter/Generic.php, line 34

Namespace

Drupal\drupalmoduleupgrader\Plugin\DMU\Rewriter
View source
class Generic extends PluginBase implements RewriterInterface {

  /**
   * @var \Drupal\drupalmoduleupgrader\Utility\Filter\NodeAssignmentFilter
   */
  protected $isAssigned;
  public function __construct(array $configuration, $plugin_id, $plugin_definition, TranslationInterface $translator, LoggerInterface $log) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $translator, $log);
    $this->isAssigned = new NodeAssignmentFilter();
  }

  /**
   * {@inheritdoc}
   */
  public function rewrite(ParameterNode $parameter) {

    // Don't even try to rewrite the function if the parameter is reassigned.
    if ($this
      ->isReassigned($parameter)) {
      $error = $this
        ->t('@function() cannot be parametrically rewritten because @parameter is reassigned.', [
        '@parameter' => $parameter
          ->getName(),
        '@function' => $parameter
          ->getFunction()
          ->getName()
          ->getText(),
      ]);
      throw new \LogicException($error);
    }
    foreach ($this
      ->getExpressions($parameter)
      ->not($this->isAssigned) as $expr) {
      $property = $this
        ->getProperty($expr);
      if (empty($property)) {
        continue;
      }
      $getter = $this
        ->rewriteAsGetter($expr, $property);
      if ($getter) {
        $empty = $expr
          ->closest(Filter::isFunctionCall('empty', 'isset'));

        // If the original expression was wrapped by a call to isset() or
        // empty(), we need to replace it entirely.
        if ($getter instanceof CallNode && $empty instanceof CallNode) {

          // If the isset() or empty() call was negated, reverse that logic.
          $parent = $empty
            ->parent();
          if ($parent instanceof BooleanNotNode) {
            $parent
              ->replaceWith($getter);
          }
          else {
            $empty
              ->replaceWith(BooleanNotNode::fromExpression($getter));
          }
        }
        else {
          $expr
            ->replaceWith($getter);
        }
      }
    }
    foreach ($this
      ->getExpressions($parameter)
      ->filter($this->isAssigned) as $expr) {

      // If the property cannot be determined, don't even try to rewrite the
      // expression.
      $property = $this
        ->getProperty($expr);
      if (empty($property)) {
        continue;
      }
      $assignment = $expr
        ->closest(Filter::isInstanceOf('\\Pharborist\\Operators\\AssignNode'));
      $setter = $this
        ->rewriteAsSetter($expr, $property, $assignment);
      if ($setter) {
        $assignment
          ->replaceWith($setter);
      }
    }

    // Set the type hint, if one is defined.
    if (isset($this->pluginDefinition['type_hint'])) {
      $parameter
        ->setTypeHint($this->pluginDefinition['type_hint']);

      // If the type hint extends FieldableEntityInterface, rewrite any field
      // lookups (e.g. $node->body[LANGUAGE_NONE][0]['value']).
      if (in_array('Drupal\\Core\\Entity\\FieldableEntityInterface', class_implements($this->pluginDefinition['type_hint']))) {
        $filter = new FieldValueFilter($parameter
          ->getName());
        foreach ($parameter
          ->getFunction()
          ->find($filter) as $lookup) {
          $lookup
            ->replaceWith(self::rewriteFieldLookup($lookup));
        }
      }
    }
  }

  /**
   * Finds every rewritable expression in the function body.
   *
   * @param \Pharborist\Functions\ParameterNode $parameter
   *   The parameter on which the rewrite is based.
   *
   * @return \Pharborist\NodeCollection
   */
  protected function getExpressions(ParameterNode $parameter) {
    $filter = Filter::isInstanceOf('\\Pharborist\\ArrayLookupNode', '\\Pharborist\\Objects\\ObjectPropertyNode');
    $expressions = new NodeCollection();
    $parameter
      ->getFunction()
      ->find(Filter::isInstanceOf('\\Pharborist\\Variables\\VariableNode'))
      ->filter(function (VariableNode $variable) use ($parameter) {
      return $variable
        ->getName() == $parameter
        ->getName();
    })
      ->each(function (VariableNode $variable) use ($filter, $expressions) {
      $root = $variable
        ->furthest($filter);
      if ($root) {
        $expressions
          ->add($root);
      }
    });
    return $expressions;
  }

  /**
   * Returns the property used by a rewritable expression, or NULL if the
   * property cannot be determined.
   *
   * @param \Pharborist\ExpressionNode $expr
   *   The rewritable expression.
   *
   * @return string|null
   */
  protected function getProperty(ExpressionNode $expr) {
    if ($expr instanceof ObjectPropertyNode) {
      return $expr
        ->getPropertyName();
    }
    elseif ($expr instanceof ArrayLookupNode) {
      $key = $expr
        ->getKey(0);
      if ($key instanceof StringNode) {
        return $key
          ->toValue();
      }
    }
  }

  /**
   * Rewrites the given expression as a property getter. Returns NULL if the
   * expression cannot be rewritten.
   *
   * @param \Pharborist\ExpressionNode $expr
   *   The expression to rewrite.
   * @param string $property
   *   The property being used in the expression.
   *
   * @return \Pharborist\ExpressionNode|null
   */
  public function rewriteAsGetter(ExpressionNode $expr, $property) {
    if ($expr instanceof ObjectPropertyNode) {

      // Should be getRootObject() or getLookupRoot().
      // @see Pharborist issue #191
      $object = clone $expr
        ->getObject();
    }
    elseif ($expr instanceof ArrayLookupNode) {
      $object = clone $expr
        ->getRootArray();
    }
    if (isset($object) && isset($this->pluginDefinition['properties'][$property]['get'])) {
      return ObjectMethodCallNode::create($object, $this->pluginDefinition['properties'][$property]['get']);
    }
  }

  /**
   * Rewrites an assignment expression as a property setter. Returns NULL if
   * the expression cannot be rewritten.
   *
   * @param \Pharborist\ExpressionNode $expr
   *   The expression to rewrite.
   * @param string $property
   *   The property being used in the expression.
   * @param \Pharborist\Operators\AssignNode $assignment
   *   The entire assignment expression being rewritten.
   *
   * @return \Pharborist\ExpressionNode|null
   */
  public function rewriteAsSetter(ExpressionNode $expr, $property, AssignNode $assignment) {
    if ($expr instanceof ObjectPropertyNode) {

      // Should be getRootObject() or getLookupRoot().
      // @see Pharborist issue #191
      $object = clone $expr
        ->getObject();
    }
    elseif ($expr instanceof ArrayLookupNode) {
      $object = clone $expr
        ->getRootArray();
    }
    if (isset($object) && isset($this->pluginDefinition['properties'][$property]['set'])) {
      return ObjectMethodCallNode::create($object, $this->pluginDefinition['properties'][$property]['set'])
        ->appendArgument(clone $assignment
        ->getRightOperand());
    }
  }

  /**
   * Returns if the parameter is fully reassigned anywhere in the function.
   *
   * @param \Pharborist\Functions\ParameterNode $parameter
   *   The parameter to check.
   *
   * @return bool
   */
  protected function isReassigned(ParameterNode $parameter) {
    return (bool) $parameter
      ->getFunction()
      ->find(Filter::isInstanceOf('\\Pharborist\\Variables\\VariableNode'))
      ->filter(function (VariableNode $variable) use ($parameter) {
      return $variable
        ->getName() == $parameter
        ->getName();
    })
      ->filter($this->isAssigned)
      ->count();
  }

  /**
   * Rewrites a Drupal 7 field lookup like so:
   *
   * $node->body[LANGUAGE_NONE][0]['value'] --> $node->body[0]->value
   * $node->body['fr'][0]['value'] --> $node->getTranslation('fr')->body[0]->value
   *
   * @param \Pharborist\ArrayLookupNode $node
   *   The original field lookup.
   *
   * @return \Pharborist\ExpressionNode
   */
  public static function rewriteFieldLookup(ArrayLookupNode $node) {
    $keys = $node
      ->getKeys();

    /** @var \Pharborist\Objects\ObjectPropertyNode $root */
    $root = $node
      ->getRootArray();
    $expr = $root
      ->getObject()
      ->getText();
    if (self::isTranslation($keys[0])) {
      $expr .= '->getTranslation(' . $keys[0] . ')';
    }
    $expr .= '->' . $root
      ->getPropertyName() . '[' . $keys[1] . ']';

    /** @var \Pharborist\Types\StringNode|\Pharborist\Node $column */
    foreach (array_slice($keys, 2) as $column) {
      $expr .= '->';
      $expr .= $column instanceof StringNode ? $column
        ->toValue() : $column
        ->getText();
    }
    return Parser::parseExpression($expr);
  }

  /**
   * Checks if a field lookup key is translated. This will be TRUE unless one
   * of the following conditions applies:
   *
   * - The key is the Drupal\Core\Language\Language::LANGCODE_NOT_SPECIFIED
   *   constant.
   * - The key is the LANGUAGE_NONE constant from Drupal 7.
   * - The key is the string 'und'.
   *
   * @param \Pharborist\Node $key
   *   The key to check.
   *
   * @return bool
   */
  public static function isTranslation(Node $key) {
    if ($key instanceof ClassConstantLookupNode) {
      $constant = $key
        ->getClassName() . '::' . $key
        ->getConstantName();
      return $constant != '\\Drupal\\Core\\Language\\Language::LANGCODE_NOT_SPECIFIED';
    }
    elseif ($key instanceof ConstantNode) {
      return $key
        ->getConstantName() != 'LANGUAGE_NONE';
    }
    elseif ($key instanceof StringNode) {
      return $key
        ->toValue() != 'und';
    }
    else {
      return TRUE;
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
Generic::$isAssigned protected property
Generic::getExpressions protected function Finds every rewritable expression in the function body.
Generic::getProperty protected function Returns the property used by a rewritable expression, or NULL if the property cannot be determined.
Generic::isReassigned protected function Returns if the parameter is fully reassigned anywhere in the function.
Generic::isTranslation public static function Checks if a field lookup key is translated. This will be TRUE unless one of the following conditions applies:
Generic::rewrite public function Parametrically rewrites the function containing the given parameter. Overrides RewriterInterface::rewrite 1
Generic::rewriteAsGetter public function Rewrites the given expression as a property getter. Returns NULL if the expression cannot be rewritten. 1
Generic::rewriteAsSetter public function Rewrites an assignment expression as a property setter. Returns NULL if the expression cannot be rewritten. 1
Generic::rewriteFieldLookup public static function Rewrites a Drupal 7 field lookup like so:
Generic::__construct public function Constructs a \Drupal\Component\Plugin\PluginBase object. Overrides PluginBase::__construct
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$log protected property
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::create public static function Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface::create 2
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.