Further modules included.
[yaffs-website] / web / modules / contrib / drupalmoduleupgrader / src / ConverterBase.php
diff --git a/web/modules/contrib/drupalmoduleupgrader/src/ConverterBase.php b/web/modules/contrib/drupalmoduleupgrader/src/ConverterBase.php
new file mode 100644 (file)
index 0000000..8b2c4e4
--- /dev/null
@@ -0,0 +1,285 @@
+<?php
+
+namespace Drupal\drupalmoduleupgrader;
+
+use Drupal\Component\Serialization\Yaml;
+use Drupal\drupalmoduleupgrader\Utility\Filter\ContainsLogicFilter;
+use Drupal\drupalmoduleupgrader\Utility\Filter\FunctionCallArgumentFilter;
+use Pharborist\DocCommentNode;
+use Pharborist\Filter;
+use Pharborist\Functions\FunctionCallNode;
+use Pharborist\Functions\FunctionDeclarationNode;
+use Pharborist\Functions\ParameterNode;
+use Pharborist\LineCommentBlockNode;
+use Pharborist\Objects\ClassNode;
+use Pharborist\Parser;
+use Pharborist\Variables\VariableNode;
+use Pharborist\WhitespaceNode;
+use Symfony\Component\Filesystem\Filesystem;
+
+/**
+ * Base class for converters.
+ */
+abstract class ConverterBase extends PluginBase implements ConverterInterface {
+
+  // Used by buildFixMe() to determine the comment style of the generated
+  // FIXME notice.
+  const LINE_COMMENT = '//';
+  const DOC_COMMENT = '/**/';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isExecutable(TargetInterface $target) {
+    // If the plugin applies to particular hook(s), only return TRUE if the
+    // target module implements any of the hooks. Otherwise, return TRUE
+    // unconditionally.
+    if (isset($this->pluginDefinition['hook'])) {
+      return (boolean) array_filter((array) $this->pluginDefinition['hook'], [ $target->getIndexer('function'), 'has' ]);
+    }
+    else {
+      return TRUE;
+    }
+  }
+
+  /**
+   * Executes the target module's implementation of the specified hook, and
+   * returns the result.
+   *
+   * @return mixed
+   *
+   * @throws \LogicException if the target module doesn't implement the
+   * specified hook, or if the implementation contains logic.
+   *
+   * @deprecated
+   */
+  protected function executeHook(TargetInterface $target, $hook) {
+    $indexer = $target->getIndexer('function');
+
+    if ($indexer->has($hook)) {
+      // Configure the ContainsLogicFilter so that certain "safe" functions
+      // will pass it.
+      $has_logic = new ContainsLogicFilter();
+      $has_logic->whitelist('t');
+      $has_logic->whitelist('drupal_get_path');
+
+      $function = $indexer->get($hook);
+      if ($function->is($has_logic)) {
+        throw new \LogicException('{target}_{hook} cannot be executed because it contains logic.');
+      }
+      else {
+        $function_name = $function->getName()->getText();
+        if (! function_exists($function_name)) {
+          eval($function->getText());
+        }
+        return call_user_func($function_name);
+      }
+    }
+    else {
+      throw new \LogicException('{target} does not implement hook_{hook}.');
+    }
+  }
+
+  /**
+   * Creates an empty implementation of a hook.
+   *
+   * @param TargetInterface $target
+   *  The target module.
+   * @param string $hook
+   *  The hook to implement, without the hook_ prefix.
+   *
+   * @return \Pharborist\Functions\FunctionDeclarationNode
+   *  The hook implementation, appended to the main module file.
+   */
+  protected function implement(TargetInterface $target, $hook) {
+    $function = FunctionDeclarationNode::create($target->id() . '_' . $hook);
+    $function->setDocComment(DocCommentNode::create('Implements hook_' . $hook . '().'));
+
+    $module_file = $target->getPath('.module');
+    $target->open($module_file)->append($function);
+
+    WhitespaceNode::create("\n")->insertBefore($function);
+    WhitespaceNode::create("\n")->insertAfter($function);
+
+    return $function;
+  }
+
+  /**
+   * Writes a file to the target module's directory.
+   *
+   * @param TargetInterface $target
+   *  The target module.
+   * @param string $path
+   *  The path of the file to write, relative to the module root.
+   * @param string $data
+   *  The file contents.
+   *
+   * @return string
+   *  The path of the file, including the target's base path.
+   */
+  public function write(TargetInterface $target, $path, $data) {
+    static $fs;
+    if (empty($fs)) {
+      $fs = new Filesystem();
+    }
+
+    $destination_path = $target->getPath($path);
+    $fs->dumpFile($destination_path, (string) $data);
+
+    return $destination_path;
+  }
+
+  /**
+   * Writes a class to the target module's PSR-4 root.
+   *
+   * @param TargetInterface $target
+   *  The target module.
+   * @param ClassNode $class
+   *  The class to write. The path will be determined from the class'
+   *  fully qualified name.
+   *
+   * @return string
+   *  The generated path to the class.
+   */
+  public function writeClass(TargetInterface $target, ClassNode $class) {
+    $class_path = ltrim($class->getName()->getAbsolutePath(), '\\');
+    $path = str_replace([ 'Drupal\\' . $target->id(), '\\', ], [ 'src', '/' ], $class_path) . '.php';
+
+    return $this->write($target, $path, $class->parents()->get(0));
+  }
+
+  /**
+   * Writes out arbitrary data in YAML format.
+   *
+   * @param TargetInterface $target
+   *  The target module.
+   * @param string $group
+   *  The name of the YAML file. It will be prefixed with the module's machine
+   *  name and suffixed with .yml. For example, a group value of 'routing'
+   *  will write MODULE.routing.yml.
+   * @param array $data
+   *  The data to write.
+   *
+   * @todo This should be writeYAML, not writeInfo.
+   */
+  protected function writeInfo(TargetInterface $target, $group, array $data) {
+    $destination = $target->getPath('.' . $group . '.yml');
+    file_put_contents($destination, Yaml::encode($data));
+  }
+
+  /**
+   * Writes a service definition to the target module's services.yml file.
+   *
+   * @param TargetInterface $target
+   *  The target module.
+   * @param string $service_id
+   *  The service ID. If an existing one with the same ID already exists,
+   *  it will be overwritten.
+   * @param array $service_definition
+   */
+  protected function writeService(TargetInterface $target, $service_id, array $service_definition) {
+    $services = $target->getServices();
+    $services->set($service_id, $service_definition);
+    $this->writeInfo($target, 'services', [ 'services' => $services->toArray() ]);
+  }
+
+  /**
+   * Parses a generated class into a syntax tree.
+   *
+   * @param string|array $class
+   *  The class to parse, either as a string of PHP code or a renderable array.
+   *
+   * @return \Pharborist\Objects\ClassNode
+   */
+  protected function parse($class) {
+    if (is_array($class)) {
+      $class = \Drupal::service('renderer')->renderPlain($class);
+    }
+    return Parser::parseSnippet($class)->find(Filter::isInstanceOf('Pharborist\Objects\ClassNode'))[0];
+  }
+
+  /**
+   * Builds a FIXME notice using either the text in the plugin definition,
+   * or passed-in text.
+   *
+   * @param string|NULL $text
+   *  The FIXME notice's text, with variable placeholders and no translation.
+   * @param array $variables
+   *  Optional variables to use in translation. If empty, the FIXME will not
+   *  be translated.
+   * @param string|NULL $style
+   *  The comment style. Returns a LineCommentBlockNode if this is set to
+   *  self::LINE_COMMENT, a DocCommentNode if self::DOC_COMMENT, or the FIXME
+   *  as a string if set to anything else.
+   *
+   * @return mixed
+   */
+  protected function buildFixMe($text = NULL, array $variables = [], $style = self::LINE_COMMENT) {
+    $fixMe = "@FIXME\n" . ($text ?: $this->pluginDefinition['fixme']);
+
+    if (isset($this->pluginDefinition['documentation'])) {
+      $fixMe .= "\n";
+      foreach ($this->pluginDefinition['documentation'] as $doc) {
+        $fixMe .= "\n@see ";
+        $fixMe .= (isset($doc['url']) ? $doc['url'] : (string) $doc);
+      }
+    }
+
+    if ($variables) {
+      $fixMe = $this->t($fixMe, $variables);
+    }
+
+    switch ($style) {
+      case self::LINE_COMMENT:
+        return LineCommentBlockNode::create($fixMe);
+
+      case self::DOC_COMMENT:
+        return DocCommentNode::create($fixMe);
+
+      default:
+        return $fixMe;
+    }
+  }
+
+  /**
+   * Parametrically rewrites a function.
+   *
+   * @param \Drupal\drupalmoduleupgrader\RewriterInterface $rewriter
+   *  A fully configured parametric rewriter.
+   * @param \Pharborist\Functions\ParameterNode $parameter
+   *  The parameter upon which to base the rewrite.
+   * @param TargetInterface $target
+   *  The target module.
+   * @param boolean $recursive
+   *  If TRUE, rewriting will recurse into called functions which are passed
+   *  the rewritten parameter as an argument.
+   */
+  protected function rewriteFunction(RewriterInterface $rewriter, ParameterNode $parameter, TargetInterface $target, $recursive = TRUE) {
+    $rewriter->rewrite($parameter);
+    $target->save($parameter);
+
+    // Find function calls within the rewritten function which are called
+    // with the rewritten parameter.
+    $indexer = $target->getIndexer('function');
+    $next = $parameter
+      ->getFunction()
+      ->find(new FunctionCallArgumentFilter($parameter->getName()))
+      ->filter(function(FunctionCallNode $call) use ($indexer) {
+        return $indexer->has($call->getName()->getText());
+      });
+
+    /** @var \Pharborist\Functions\FunctionCallNode $call */
+    foreach ($next as $call) {
+      /** @var \Pharborist\Functions\FunctionDeclarationNode $function */
+      $function = $indexer->get($call->getName()->getText());
+
+      foreach ($call->getArguments() as $index => $argument) {
+        if ($argument instanceof VariableNode && $argument->getName() == $parameter->getName()) {
+          $this->rewriteFunction($rewriter, $function->getParameterAtIndex($index), $target, $recursive);
+          break;
+        }
+      }
+    }
+  }
+
+}