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; } } } } }