30a6b26126a56b9186638e816dfed380c1aa5b67
[yaffs-website] / web / modules / contrib / drupalmoduleupgrader / src / Target.php
1 <?php
2
3 namespace Drupal\drupalmoduleupgrader;
4
5 use Doctrine\Common\Collections\ArrayCollection;
6 use Drupal\Component\Utility\SafeMarkup;
7 use Pharborist\Node;
8 use Pharborist\Parser;
9 use Pharborist\RootNode;
10 use Symfony\Component\DependencyInjection\ContainerInterface;
11 use Symfony\Component\Finder\Finder;
12
13 /**
14  * Default implementation of TargetInterface.
15  */
16 class Target implements TargetInterface {
17
18   /**
19    * The target module's machine name.
20    *
21    * @var string
22    */
23   protected $id;
24
25   /**
26    * @var \Drupal\Component\Plugin\PluginManagerInterface
27    */
28   protected $indexerManager;
29
30   /**
31    * The target module's base path.
32    *
33    * @var string
34    */
35   protected $basePath;
36
37   /**
38    * @var IndexerInterface[]
39    */
40   protected $indexers = [];
41
42   /**
43    * @var \Doctrine\Common\Collections\ArrayCollection
44    */
45   protected $services;
46
47   /**
48    * All open documents.
49    *
50    * @var \Pharborist\RootNode[]
51    */
52   protected $documents = [];
53
54   /**
55    * Constructs a Target.
56    *
57    * @param string $path
58    *  The base path of the target module.
59    * @param ContainerInterface $container
60    *  The current container, to pull any dependencies out of.
61    */
62   public function __construct($path, ContainerInterface $container) {
63     $this->indexerManager = $container->get('plugin.manager.drupalmoduleupgrader.indexer');
64
65     if (is_dir($path)) {
66       $this->basePath = $path;
67     }
68     else {
69       throw new \RuntimeException(SafeMarkup::format('Invalid base path: @path', ['@path' => $path]));
70     }
71   }
72
73   /**
74    * {@inheritdoc}
75    */
76   public function id() {
77     if (empty($this->id)) {
78       $dir = $this->getBasePath();
79       $info = (new Finder)->in($dir)->depth('== 0')->name('*.info')->getIterator();
80       $info->rewind();
81
82       if ($info_file = $info->current()) {
83         $this->id = subStr($info_file->getFilename(), 0, -5);
84       }
85       else {
86         throw new \RuntimeException(SafeMarkup::format('Could not find info file in @dir', ['@dir' => $dir]));
87       }
88     }
89     return $this->id;
90   }
91
92   /**
93    * {@inheritdoc}
94    */
95   public function getBasePath() {
96     return $this->basePath;
97   }
98
99   /**
100    * {@inheritdoc}
101    */
102   public function getPath($file) {
103     if ($file{0} == '.') {
104       $file = $this->id() . $file;
105     }
106     return $this->getBasePath() . '/' . ltrim($file, '/');
107   }
108
109   /**
110    * {@inheritdoc}
111    */
112   public function getFinder() {
113       // We do NOT want to include submodules. We can detect one by the presence
114       // of an info file -- if there is one, its directory is a submodule.
115       $directories = (new Finder)
116         ->directories()
117         ->in($this->getBasePath())
118         ->filter(function(\SplFileInfo $dir) {
119           return (new Finder)->files()->in($dir->getPathname())->depth('== 0')->name('*.info')->count() === 0;
120         });
121
122       $directories = array_keys(iterator_to_array($directories));
123       $directories[] = $this->getBasePath();
124
125       return (new Finder)
126         ->files()
127         ->in($directories)
128         // We don't need to recurse, because we've already determined which
129         // directories to search.
130         ->depth('== 0')
131         ->name('*.module')
132         ->name('*.install')
133         ->name('*.inc')
134         ->name('*.php')
135         ->name('*.test');
136   }
137
138   /**
139    * {@inheritdoc}
140    */
141   public function getIndexer($which) {
142     if (empty($this->indexers[$which])) {
143       /** @var IndexerInterface $indexer */
144       $indexer = $this->indexerManager->createInstance($which);
145       $indexer->bind($this);
146       $this->indexers[$which] = $indexer;
147     }
148     return $this->indexers[$which];
149   }
150
151   /**
152    * {@inheritdoc}
153    */
154   public function getServices() {
155     if (empty($this->services)) {
156       $this->services = new ArrayCollection();
157     }
158     return $this->services;
159   }
160
161   /**
162    * Runs all available indexers on this target.
163    */
164   public function buildIndex() {
165     $indexers = array_keys($this->indexerManager->getDefinitions());
166     foreach ($indexers as $id) {
167       $this->getIndexer($id)->build();
168     }
169     // Release syntax trees that were opened during indexing.
170     $this->flush();
171   }
172
173   /**
174    * Destroys all index data for this target.
175    */
176   public function destroyIndex() {
177     $indexers = array_keys($this->indexerManager->getDefinitions());
178     foreach ($indexers as $id) {
179       $this->getIndexer($id)->destroy();
180     }
181   }
182
183   /**
184    * {@inheritdoc}
185    */
186   public function implementsHook($hook) {
187     return $this->getIndexer('function')->has('hook_' . $hook);
188   }
189
190   /**
191    * {@inheritdoc}
192    */
193   public function executeHook($hook, array $arguments = []) {
194     if ($this->implementsHook($hook)) {
195       return $this->getIndexer('function')->execute('hook_' . $hook, $arguments);
196     }
197     else {
198       $variables = [
199         '@module' => $this->id(),
200         '@hook' => $hook,
201       ];
202       throw new \InvalidArgumentException(SafeMarkup::format('@module does not implement hook_@hook.', $variables));
203     }
204   }
205
206   /**
207    * {@inheritdoc}
208    */
209   public function open($file) {
210     if (empty($this->documents[$file])) {
211       $this->documents[$file] = Parser::parseFile($file);
212     }
213     return $this->documents[$file];
214   }
215
216   /**
217    * {@inheritdoc}
218    */
219   public function save(Node $node = NULL) {
220     if ($node) {
221       $file = $this->getFileOf($node);
222       if ($file) {
223         $doc = $node instanceof RootNode ? $node : $node->parents()->get(0);
224
225         $victory = file_put_contents($file, $doc->getText());
226         if ($victory === FALSE) {
227           throw new IOException(SafeMarkup::format('Failed to save @file.', [ '@file' => $file ]));
228         }
229       }
230       else {
231         throw new IOException('Cannot save a node that is not attached to an open document.');
232       }
233     }
234     else {
235       array_walk($this->documents, [ $this, 'save' ]);
236     }
237   }
238
239   /**
240    * {@inheritdoc}
241    */
242   public function create($file, $ns = NULL) {
243     $this->documents[$file] = RootNode::create($ns);
244     return $this->documents[$file];
245   }
246
247   /**
248    * {@inheritdoc}
249    */
250   public function flush() {
251     $this->documents = [];
252   }
253
254   /**
255    * Determines which currently-open file a node belongs to, if any. Nodes
256    * which are not part of any open syntax tree will return NULL.
257    *
258    * @return string|NULL
259    */
260   public function getFileOf(Node $node) {
261     if ($node instanceof RootNode) {
262       $root = $node;
263     }
264     else {
265       $parents = $node->parents();
266       if ($parents->isEmpty()) {
267         return NULL;
268       }
269       $root = $parents->get(0);
270     }
271
272     foreach ($this->documents as $file => $doc) {
273       if ($root === $doc) {
274         return $file;
275       }
276     }
277   }
278
279 }