Total Complexity | 102 |
Total Lines | 716 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like PhingTask often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use PhingTask, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
56 | class PhingTask extends Task |
||
57 | { |
||
58 | use FileSetAware; |
||
59 | |||
60 | /** |
||
61 | * the target to call if any. |
||
62 | * |
||
63 | * @var Target |
||
64 | */ |
||
65 | protected $newTarget; |
||
66 | |||
67 | /** |
||
68 | * the basedir where is executed the build file. |
||
69 | * |
||
70 | * @var File |
||
71 | */ |
||
72 | private $dir; |
||
73 | |||
74 | /** |
||
75 | * build.xml (can be absolute) in this case dir will be ignored. |
||
76 | */ |
||
77 | private $phingFile; |
||
78 | |||
79 | /** |
||
80 | * should we inherit properties from the parent ? |
||
81 | */ |
||
82 | private $inheritAll = true; |
||
83 | |||
84 | /** |
||
85 | * should we inherit references from the parent ? |
||
86 | */ |
||
87 | private $inheritRefs = false; |
||
88 | |||
89 | /** |
||
90 | * the properties to pass to the new project. |
||
91 | */ |
||
92 | private $properties = []; |
||
93 | |||
94 | /** |
||
95 | * the references to pass to the new project. |
||
96 | * |
||
97 | * @var PhingReference[] |
||
98 | */ |
||
99 | private $references = []; |
||
100 | |||
101 | /** |
||
102 | * The temporary project created to run the build file. |
||
103 | * |
||
104 | * @var Project |
||
105 | */ |
||
106 | private $newProject; |
||
107 | |||
108 | /** |
||
109 | * Fail the build process when the called build fails? |
||
110 | */ |
||
111 | private $haltOnFailure = false; |
||
112 | |||
113 | /** |
||
114 | * Whether the basedir of the new project should be the same one |
||
115 | * as it would be when running the build file directly - |
||
116 | * independent of dir and/or inheritAll settings. |
||
117 | */ |
||
118 | private $useNativeBasedir = false; |
||
119 | |||
120 | /** |
||
121 | * @var OutputStream |
||
122 | */ |
||
123 | private $out; |
||
124 | |||
125 | /** @var string */ |
||
126 | private $output; |
||
127 | |||
128 | /** |
||
129 | * @var array |
||
130 | */ |
||
131 | private $locals; |
||
132 | |||
133 | public function __construct(?Task $owner = null) |
||
139 | } |
||
140 | |||
141 | /** |
||
142 | * If true, abort the build process if there is a problem with or in the target build file. |
||
143 | * Defaults to false. |
||
144 | * |
||
145 | * @param bool $hof new value |
||
146 | */ |
||
147 | public function setHaltOnFailure($hof) |
||
148 | { |
||
149 | $this->haltOnFailure = (bool) $hof; |
||
150 | } |
||
151 | |||
152 | /** |
||
153 | * Whether the basedir of the new project should be the same one |
||
154 | * as it would be when running the build file directly - |
||
155 | * independent of dir and/or inheritAll settings. |
||
156 | */ |
||
157 | public function setUseNativeBasedir(bool $b) |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * Creates a Project instance for the project to call. |
||
164 | */ |
||
165 | public function init() |
||
166 | { |
||
167 | $this->newProject = $this->getProject()->createSubProject(); |
||
168 | $tdf = $this->project->getTaskDefinitions(); |
||
169 | $this->newProject->addTaskDefinition('property', $tdf['property']); |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Main entry point for the task. |
||
174 | */ |
||
175 | public function main() |
||
176 | { |
||
177 | // Call Phing on the file set with the attribute "phingfile" |
||
178 | if (null !== $this->phingFile || null !== $this->dir) { |
||
179 | $this->processFile(); |
||
180 | } |
||
181 | |||
182 | // if no filesets are given stop here; else process filesets |
||
183 | if (!empty($this->filesets)) { |
||
184 | // preserve old settings |
||
185 | $savedDir = $this->dir; |
||
186 | $savedPhingFile = $this->phingFile; |
||
187 | $savedTarget = $this->newTarget; |
||
188 | |||
189 | // set no specific target for files in filesets |
||
190 | // [HL] I'm commenting this out; I don't know why this should not be supported! |
||
191 | // $this->newTarget = null; |
||
192 | |||
193 | foreach ($this->filesets as $fs) { |
||
194 | $ds = $fs->getDirectoryScanner($this->project); |
||
195 | |||
196 | $fromDir = $fs->getDir($this->project); |
||
|
|||
197 | $srcFiles = $ds->getIncludedFiles(); |
||
198 | |||
199 | foreach ($srcFiles as $fname) { |
||
200 | $f = new File($ds->getbasedir(), $fname); |
||
201 | $f = $f->getAbsoluteFile(); |
||
202 | $this->phingFile = $f->getAbsolutePath(); |
||
203 | $this->dir = $f->getParentFile(); |
||
204 | $this->processFile(); // run Phing! |
||
205 | } |
||
206 | } |
||
207 | |||
208 | // side effect free programming ;-) |
||
209 | $this->dir = $savedDir; |
||
210 | $this->phingFile = $savedPhingFile; |
||
211 | $this->newTarget = $savedTarget; |
||
212 | |||
213 | // [HL] change back to correct dir |
||
214 | if (null !== $this->dir) { |
||
215 | chdir($this->dir->getAbsolutePath()); |
||
216 | } |
||
217 | } |
||
218 | |||
219 | // Remove any dangling references to help the GC |
||
220 | foreach ($this->properties as $property) { |
||
221 | $property->setFallback(null); |
||
222 | } |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * If true, pass all properties to the new phing project. |
||
227 | * Defaults to true. |
||
228 | * |
||
229 | * @param bool $inheritAll |
||
230 | */ |
||
231 | public function setInheritAll($inheritAll) |
||
232 | { |
||
233 | $this->inheritAll = (bool) $inheritAll; |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * If true, pass all references to the new phing project. |
||
238 | * Defaults to false. |
||
239 | * |
||
240 | * @param bool $inheritRefs |
||
241 | */ |
||
242 | public function setInheritRefs($inheritRefs) |
||
243 | { |
||
244 | $this->inheritRefs = (bool) $inheritRefs; |
||
245 | } |
||
246 | |||
247 | /** |
||
248 | * The directory to use as a base directory for the new phing project. |
||
249 | * Defaults to the current project's basedir, unless inheritall |
||
250 | * has been set to false, in which case it doesn't have a default |
||
251 | * value. This will override the basedir setting of the called project. |
||
252 | */ |
||
253 | public function setDir(File $dir): void |
||
254 | { |
||
255 | $this->dir = $dir; |
||
256 | } |
||
257 | |||
258 | /** |
||
259 | * The build file to use. |
||
260 | * Defaults to "build.xml". This file is expected to be a filename relative |
||
261 | * to the dir attribute given. |
||
262 | */ |
||
263 | public function setPhingFile(string $file) |
||
264 | { |
||
265 | // it is a string and not a file to handle relative/absolute |
||
266 | // otherwise a relative file will be resolved based on the current |
||
267 | // basedir. |
||
268 | $this->phingFile = $file; |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * Alias function for setPhingfile. |
||
273 | * |
||
274 | * @param string $file |
||
275 | */ |
||
276 | public function setBuildfile($file) |
||
277 | { |
||
278 | $this->setPhingFile($file); |
||
279 | } |
||
280 | |||
281 | /** |
||
282 | * The target of the new Phing project to execute. |
||
283 | * Defaults to the new project's default target. |
||
284 | */ |
||
285 | public function setTarget(string $target) |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * Set the filename to write the output to. This is relative to the value |
||
296 | * of the dir attribute if it has been set or to the base directory of the |
||
297 | * current project otherwise. |
||
298 | * |
||
299 | * @param string $outputFile the name of the file to which the output should go |
||
300 | */ |
||
301 | public function setOutput(string $outputFile): void |
||
304 | } |
||
305 | |||
306 | /** |
||
307 | * Property to pass to the new project. |
||
308 | * The property is passed as a 'user property'. |
||
309 | */ |
||
310 | public function createProperty() |
||
311 | { |
||
312 | $p = new PropertyTask(); |
||
313 | $p->setFallback($this->getNewProject()); |
||
314 | $p->setUserProperty(true); |
||
315 | $p->setTaskName('property'); |
||
316 | $this->properties[] = $p; |
||
317 | |||
318 | return $p; |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * Reference element identifying a data type to carry |
||
323 | * over to the new project. |
||
324 | */ |
||
325 | public function addReference(PhingReference $ref) |
||
326 | { |
||
327 | $this->references[] = $ref; |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * Get the (sub)-Project instance currently in use. |
||
332 | */ |
||
333 | protected function getNewProject(): Project |
||
334 | { |
||
335 | if (null === $this->newProject) { |
||
336 | $this->reinit(); |
||
337 | } |
||
338 | |||
339 | return $this->newProject; |
||
340 | } |
||
341 | |||
342 | /** |
||
343 | * Called in execute or createProperty if newProject is null. |
||
344 | * |
||
345 | * <p>This can happen if the same instance of this task is run |
||
346 | * twice as newProject is set to null at the end of execute (to |
||
347 | * save memory and help the GC).</p> |
||
348 | * |
||
349 | * <p>Sets all properties that have been defined as nested |
||
350 | * property elements.</p> |
||
351 | */ |
||
352 | private function reinit() |
||
389 | } |
||
390 | } |
||
391 | } |
||
392 | |||
393 | /** |
||
394 | * Execute phing file. |
||
395 | * |
||
396 | * @throws BuildException |
||
397 | */ |
||
398 | private function processFile(): void |
||
542 | } |
||
543 | } |
||
544 | } |
||
545 | |||
546 | /** |
||
547 | * Configure the Project, i.e. make intance, attach build listeners |
||
548 | * (copy from father project), add Task and Datatype definitions, |
||
549 | * copy properties and references from old project if these options |
||
550 | * are set via the attributes of the XML tag. |
||
551 | * |
||
552 | * Developer note: |
||
553 | * This function replaces the old methods "init", "_reinit" and |
||
554 | * "_initializeProject". |
||
555 | */ |
||
556 | private function initializeProject() |
||
557 | { |
||
558 | $this->newProject->setInputHandler($this->project->getInputHandler()); |
||
559 | |||
560 | foreach ($this->project->getBuildListeners() as $listener) { |
||
561 | $this->newProject->addBuildListener($listener); |
||
562 | } |
||
563 | |||
564 | /* Copy things from old project. Datatypes and Tasks are always |
||
565 | * copied, properties and references only if specified so/not |
||
566 | * specified otherwise in the XML definition. |
||
567 | */ |
||
568 | // Add Datatype definitions |
||
569 | foreach ($this->project->getDataTypeDefinitions() as $typeName => $typeClass) { |
||
570 | $this->newProject->addDataTypeDefinition($typeName, $typeClass); |
||
571 | } |
||
572 | |||
573 | // Add Task definitions |
||
574 | foreach ($this->project->getTaskDefinitions() as $taskName => $taskClass) { |
||
575 | if ('Phing\Task\System\PropertyTask' === $taskClass) { |
||
576 | // we have already added this taskdef in init() |
||
577 | continue; |
||
578 | } |
||
579 | $this->newProject->addTaskDefinition($taskName, $taskClass); |
||
580 | } |
||
581 | |||
582 | if (null !== $this->output) { |
||
583 | try { |
||
584 | if (null !== $this->dir) { |
||
585 | $outfile = (new FileUtils())->resolveFile($this->dir, $this->output); |
||
586 | } else { |
||
587 | $outfile = $this->getProject()->resolveFile($this->output); |
||
588 | } |
||
589 | $this->out = new FileOutputStream($outfile); |
||
590 | $logger = new DefaultLogger(); |
||
591 | $logger->setMessageOutputLevel(Project::MSG_INFO); |
||
592 | $logger->setOutputStream($this->out); |
||
593 | $logger->setErrorStream($this->out); |
||
594 | $this->newProject->addBuildListener($logger); |
||
595 | } catch (Exception $ex) { |
||
596 | $this->log("Phing: Can't set output to " . $this->output); |
||
597 | } |
||
598 | } |
||
599 | |||
600 | if ($this->useNativeBasedir) { |
||
601 | $this->addAlmostAll($this->getProject()->getUserProperties(), 'user'); |
||
602 | } else { |
||
603 | $this->project->copyUserProperties($this->newProject); |
||
604 | } |
||
605 | |||
606 | if (!$this->inheritAll) { |
||
607 | // set System built-in properties separately, |
||
608 | // b/c we won't inherit them. |
||
609 | $this->newProject->setSystemProperties(); |
||
610 | } else { |
||
611 | $this->addAlmostAll($this->getProject()->getProperties(), 'plain'); |
||
612 | } |
||
613 | } |
||
614 | |||
615 | /** |
||
616 | * Copies all properties from the given table to the new project - |
||
617 | * omitting those that have already been set in the new project as |
||
618 | * well as properties named basedir or phing.file. |
||
619 | * |
||
620 | * @param array $props properties <code>Hashtable</code> to copy to the |
||
621 | * new project |
||
622 | * @param string $type the type of property to set (a plain Phing property, a |
||
623 | * user property or an inherited property) |
||
624 | */ |
||
625 | private function addAlmostAll(array $props, string $type): void |
||
642 | } |
||
643 | } |
||
644 | } |
||
645 | |||
646 | /** |
||
647 | * Override the properties in the new project with the one |
||
648 | * explicitly defined as nested elements here. |
||
649 | * |
||
650 | * @throws BuildException |
||
651 | */ |
||
652 | private function overrideProperties() |
||
653 | { |
||
654 | // remove duplicate properties - last property wins |
||
655 | $properties = array_reverse($this->properties); |
||
656 | $set = []; |
||
657 | foreach ($properties as $i => $p) { |
||
658 | if (null !== $p->getName() && '' !== $p->getName()) { |
||
659 | if (in_array($p->getName(), $set)) { |
||
660 | unset($this->properties[$i]); |
||
661 | } else { |
||
662 | $set[] = $p->getName(); |
||
663 | } |
||
664 | } |
||
665 | $p->setProject($this->newProject); |
||
666 | $p->main(); |
||
667 | } |
||
668 | if ($this->useNativeBasedir) { |
||
669 | $this->addAlmostAll($this->getProject()->getInheritedProperties(), 'inherited'); |
||
670 | } else { |
||
671 | $this->project->copyInheritedProperties($this->newProject); |
||
672 | } |
||
673 | } |
||
674 | |||
675 | /** |
||
676 | * Add the references explicitly defined as nested elements to the |
||
677 | * new project. Also copy over all references that don't override |
||
678 | * existing references in the new project if inheritrefs has been |
||
679 | * requested. |
||
680 | * |
||
681 | * @throws BuildException |
||
682 | */ |
||
683 | private function addReferences() |
||
684 | { |
||
685 | // parent project references |
||
686 | $projReferences = $this->project->getReferences(); |
||
687 | |||
688 | $newReferences = $this->newProject->getReferences(); |
||
689 | |||
690 | $subprojRefKeys = []; |
||
691 | |||
692 | if (count($this->references) > 0) { |
||
693 | for ($i = 0, $count = count($this->references); $i < $count; ++$i) { |
||
694 | $ref = $this->references[$i]; |
||
695 | $refid = $ref->getRefId(); |
||
696 | |||
697 | if (null === $refid) { |
||
698 | throw new BuildException('the refid attribute is required for reference elements'); |
||
699 | } |
||
700 | if (!isset($projReferences[$refid])) { |
||
701 | $this->log("Parent project doesn't contain any reference '" . $refid . "'", Project::MSG_WARN); |
||
702 | |||
703 | continue; |
||
704 | } |
||
705 | |||
706 | $subprojRefKeys[] = $refid; |
||
707 | unset($this->references[$i]); //thisReferences.remove(refid); |
||
708 | $toRefid = $ref->getToRefid(); |
||
709 | if (null === $toRefid) { |
||
710 | $toRefid = $refid; |
||
711 | } |
||
712 | $this->copyReference($refid, $toRefid); |
||
713 | } |
||
714 | } |
||
715 | |||
716 | // Now add all references that are not defined in the |
||
717 | // subproject, if inheritRefs is true |
||
718 | if ($this->inheritRefs) { |
||
719 | // get the keys that are were not used by the subproject |
||
720 | $unusedRefKeys = array_diff(array_keys($projReferences), $subprojRefKeys); |
||
721 | |||
722 | foreach ($unusedRefKeys as $key) { |
||
723 | if (isset($newReferences[$key])) { |
||
724 | continue; |
||
725 | } |
||
726 | $this->copyReference($key, $key); |
||
727 | } |
||
728 | } |
||
729 | } |
||
730 | |||
731 | /** |
||
732 | * Try to clone and reconfigure the object referenced by oldkey in |
||
733 | * the parent project and add it to the new project with the key |
||
734 | * newkey. |
||
735 | * |
||
736 | * <p>If we cannot clone it, copy the referenced object itself and |
||
737 | * keep our fingers crossed.</p> |
||
738 | * |
||
739 | * @param string $oldKey |
||
740 | * @param string $newKey |
||
741 | * |
||
742 | * @throws BuildException |
||
743 | */ |
||
744 | private function copyReference($oldKey, $newKey) |
||
772 | } |
||
773 | } |
||
774 |