3 namespace Drupal\drupalmoduleupgrader\Utility;
5 use Drupal\drupalmoduleupgrader\RewriterInterface;
6 use Drupal\drupalmoduleupgrader\TargetInterface;
8 use Pharborist\Functions\FunctionCallNode;
9 use Pharborist\Functions\FunctionDeclarationNode;
10 use Pharborist\Functions\ParameterNode;
11 use Pharborist\Objects\ClassMethodNode;
12 use Pharborist\Objects\ClassNode;
13 use Pharborist\Parser;
15 use Pharborist\TokenNode;
18 * Converts a form from a set of callback functions to a class implementing
19 * \Drupal\Core\Form\FormInterface.
23 use StringTransformTrait;
26 * @var \Drupal\drupalmoduleupgrader\TargetInterface
36 * @var \Pharborist\Functions\FunctionDeclarationNode
41 * @var \Pharborist\Functions\FunctionDeclarationNode
46 * @var \Pharborist\Functions\FunctionDeclarationNode
48 protected $submitHandler;
56 * @var \Drupal\drupalmoduleupgrader\RewriterInterface
58 protected $formStateRewriter;
61 * @var \Pharborist\Objects\ClassNode
63 protected $controller;
65 public function __construct(TargetInterface $target, $form_id, RewriterInterface $rewriter) {
66 $indexer = $target->getIndexer('function');
68 $this->target = $target;
69 $this->formID = $form_id;
71 $this->builder = $indexer->get($form_id);
73 $validator = $form_id . '_validate';
74 if ($indexer->has($validator)) {
75 $this->validator = $indexer->get($validator);
77 $submit_handler = $form_id . '_submit';
78 if ($indexer->has($submit_handler)) {
79 $this->submitHandler = $indexer->get($submit_handler);
82 $this->isConfig = $this->builder->has(Filter::isFunctionCall('system_settings_form'));
83 $this->formStateRewriter = $rewriter;
87 * @return \Pharborist\Objects\ClassNode
89 public function render() {
90 if (empty($this->controller)) {
92 '#theme' => 'dmu_form',
93 '#module' => $this->target->id(),
94 '#form_id' => $this->formID,
95 '#class' => $this->toTitleCase($this->formID),
96 '#config' => $this->isConfig,
98 $source = \Drupal::service('renderer')->renderPlain($render);
99 $this->controller = Parser::parseSource($source)
100 ->find(Filter::isClass($render['#class']))->get(0);
102 return $this->controller;
106 * @return \Pharborist\Objects\ClassNode
108 public function build() {
109 $controller = $this->render();
111 $builder = $this->addMethod($this->builder, $controller, 'buildForm');
112 if ($this->isConfig) {
114 ->find(Filter::isFunctionCall('system_settings_form'))
115 ->each(function(FunctionCallNode $call) {
117 ->setName('parent::buildForm')
118 ->appendArgument(Token::variable('$form_state'));
122 if ($this->validator) {
124 ->addMethod($this->validator, $controller, 'validateForm')
125 ->getParameterAtIndex(0)
127 ->setTypeHint('array');
129 if ($this->submitHandler) {
131 ->addMethod($this->submitHandler, $controller, ($this->isConfig ? '_submitForm' : 'submitForm'))
132 ->getParameterAtIndex(0)
134 ->setTypeHint('array');
141 * @return \Pharborist\Objects\ClassMethodNode
143 protected function addMethod(FunctionDeclarationNode $function, ClassNode $class, $alias = NULL) {
144 $method = ClassMethodNode::fromFunction($function);
146 $method->setName($alias);
148 $class->appendMethod($method);
150 // Add the parameters required for FormInterface conformance.
151 $parameters = $method->getParameters()->toArray();
152 if (empty($parameters)) {
153 $parameters[0] = ParameterNode::create('$form');
154 $method->appendParameter($parameters[0]);
156 if (sizeof($parameters) == 1) {
157 $parameters[1] = ParameterNode::create('$form_state');
158 $method->appendParameter($parameters[1]);
161 // The $form parameter must have the array type hint.
162 $parameters[0]->setTypeHint('array');
164 // The form state is never passed by reference.
165 $parameters[1]->setReference(FALSE);
167 // Additional parameters MUST have a default value of NULL in order to conform
169 for ($i = 2; $i < sizeof($parameters); $i++) {
170 $parameters[$i]->setValue(new TokenNode(T_STRING, 'NULL'));
173 $this->formStateRewriter->rewrite($parameters[1]);