annotate vendor/psy/psysh/src/Psy/CodeCleaner/UseStatementPass.php @ 7:848c88cfe644

More layout
author Chris Cannam
date Fri, 05 Jan 2018 13:59:44 +0000
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 /*
Chris@0 4 * This file is part of Psy Shell.
Chris@0 5 *
Chris@0 6 * (c) 2012-2017 Justin Hileman
Chris@0 7 *
Chris@0 8 * For the full copyright and license information, please view the LICENSE
Chris@0 9 * file that was distributed with this source code.
Chris@0 10 */
Chris@0 11
Chris@0 12 namespace Psy\CodeCleaner;
Chris@0 13
Chris@0 14 use PhpParser\Node;
Chris@0 15 use PhpParser\Node\Name;
Chris@0 16 use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
Chris@0 17 use PhpParser\Node\Stmt\GroupUse;
Chris@0 18 use PhpParser\Node\Stmt\Namespace_;
Chris@0 19 use PhpParser\Node\Stmt\Use_;
Chris@0 20
Chris@0 21 /**
Chris@0 22 * Provide implicit use statements for subsequent execution.
Chris@0 23 *
Chris@0 24 * The use statement pass remembers the last use statement line encountered:
Chris@0 25 *
Chris@0 26 * use Foo\Bar as Baz;
Chris@0 27 *
Chris@0 28 * ... which it then applies implicitly to all future evaluated code, until the
Chris@0 29 * current namespace is replaced by another namespace.
Chris@0 30 */
Chris@0 31 class UseStatementPass extends CodeCleanerPass
Chris@0 32 {
Chris@0 33 private $aliases = array();
Chris@0 34 private $lastAliases = array();
Chris@0 35 private $lastNamespace = null;
Chris@0 36
Chris@0 37 /**
Chris@0 38 * Re-load the last set of use statements on re-entering a namespace.
Chris@0 39 *
Chris@0 40 * This isn't how namespaces normally work, but because PsySH has to spin
Chris@0 41 * up a new namespace for every line of code, we do this to make things
Chris@0 42 * work like you'd expect.
Chris@0 43 *
Chris@0 44 * @param Node $node
Chris@0 45 */
Chris@0 46 public function enterNode(Node $node)
Chris@0 47 {
Chris@0 48 if ($node instanceof Namespace_) {
Chris@0 49 // If this is the same namespace as last namespace, let's do ourselves
Chris@0 50 // a favor and reload all the aliases...
Chris@0 51 if (strtolower($node->name) === strtolower($this->lastNamespace)) {
Chris@0 52 $this->aliases = $this->lastAliases;
Chris@0 53 }
Chris@0 54 }
Chris@0 55 }
Chris@0 56
Chris@0 57 /**
Chris@0 58 * If this statement is a namespace, forget all the aliases we had.
Chris@0 59 *
Chris@0 60 * If it's a use statement, remember the alias for later. Otherwise, apply
Chris@0 61 * remembered aliases to the code.
Chris@0 62 *
Chris@0 63 * @param Node $node
Chris@0 64 */
Chris@0 65 public function leaveNode(Node $node)
Chris@0 66 {
Chris@0 67 if ($node instanceof Use_) {
Chris@0 68 // Store a reference to every "use" statement, because we'll need
Chris@0 69 // them in a bit.
Chris@0 70 foreach ($node->uses as $use) {
Chris@0 71 $this->aliases[strtolower($use->alias)] = $use->name;
Chris@0 72 }
Chris@0 73
Chris@0 74 return false;
Chris@0 75 } elseif ($node instanceof GroupUse) {
Chris@0 76 // Expand every "use" statement in the group into a full, standalone
Chris@0 77 // "use" and store 'em with the others.
Chris@0 78 foreach ($node->uses as $use) {
Chris@0 79 $this->aliases[strtolower($use->alias)] = Name::concat($node->prefix, $use->name, array(
Chris@0 80 'startLine' => $node->prefix->getAttribute('startLine'),
Chris@0 81 'endLine' => $use->name->getAttribute('endLine'),
Chris@0 82 ));
Chris@0 83 }
Chris@0 84
Chris@0 85 return false;
Chris@0 86 } elseif ($node instanceof Namespace_) {
Chris@0 87 // Start fresh, since we're done with this namespace.
Chris@0 88 $this->lastNamespace = $node->name;
Chris@0 89 $this->lastAliases = $this->aliases;
Chris@0 90 $this->aliases = array();
Chris@0 91 } else {
Chris@0 92 foreach ($node as $name => $subNode) {
Chris@0 93 if ($subNode instanceof Name) {
Chris@0 94 // Implicitly thunk all aliases.
Chris@0 95 if ($replacement = $this->findAlias($subNode)) {
Chris@0 96 $node->$name = $replacement;
Chris@0 97 }
Chris@0 98 }
Chris@0 99 }
Chris@0 100
Chris@0 101 return $node;
Chris@0 102 }
Chris@0 103 }
Chris@0 104
Chris@0 105 /**
Chris@0 106 * Find class/namespace aliases.
Chris@0 107 *
Chris@0 108 * @param Name $name
Chris@0 109 *
Chris@0 110 * @return FullyQualifiedName|null
Chris@0 111 */
Chris@0 112 private function findAlias(Name $name)
Chris@0 113 {
Chris@0 114 $that = strtolower($name);
Chris@0 115 foreach ($this->aliases as $alias => $prefix) {
Chris@0 116 if ($that === $alias) {
Chris@0 117 return new FullyQualifiedName($prefix->toString());
Chris@0 118 } elseif (substr($that, 0, strlen($alias) + 1) === $alias . '\\') {
Chris@0 119 return new FullyQualifiedName($prefix->toString() . substr($name, strlen($alias)));
Chris@0 120 }
Chris@0 121 }
Chris@0 122 }
Chris@0 123 }