Easiest way to detect/remove unused `use` statements from PHP codebase

前端 未结 3 1585
难免孤独
难免孤独 2021-02-13 06:04

I\'ve searched all over for something like this but I believe the word \"use\" is perhaps too common for any helpful results:

What is the easiest way to remove all un-us

3条回答
  •  一整个雨季
    2021-02-13 06:35

    EDIT

    I have completely rewritten it, so now it is much more powerful:

    • Traits and anonymous/lambda functions are ignored
    • Now taking care of catch blocks, class extensions and interfaces
    • Indentation and comments don't matter
    • Multiple declarations for namespace aliases work too
    • Static and object class calls are recognized as "usage" ($u->getUsages())
    • Full and half qualified usages are not treated

    The test file, class.php:

    And here the script:

    content = token_get_all(file_get_contents($file));
    
        // we don't need and want them while parsing
        $this->removeTokens(T_COMMENT);
        $this->removeTokens(T_WHITESPACE);
      }
    
      public function getUnused()
      {
        $uses   = $this->getUseStatements();
        $usages = $this->getUsages();
        $unused = array();
    
        foreach($uses as $use) {
          if (!in_array($use, $usages)) {
            $unused[] =  $use;
          }
        }
        return $unused;
      }
    
      public function getUsages()
      {
        $usages = array();
    
        foreach($this->content as $key => $token) {
    
          if (!is_string($token)) {
            $t = $this->content;
    
            // for static calls
            if ($token[0] == T_DOUBLE_COLON) {
              // only if it is NOT full or half qualified namespace
              if ($t[$key-2][0] != T_NAMESPACE) {
                $usages[] = $t[$key-1][1];
              }
            }
    
            // for object instanciations
            if ($token[0] == T_NEW) {
              if ($t[$key+2][0] != T_NAMESPACE) {
                $usages[] = $t[$key+1][1];
              }
            }
    
            // for class extensions
            if ($token[0] == T_EXTENDS || $token[0] == T_IMPLEMENTS) {
              if ($t[$key+2][0] != T_NAMESPACE) {
                $usages[] = $t[$key+1][1];
              }
            }
    
            // for catch blocks
            if ($token[0] == T_CATCH) {
              if ($t[$key+3][0] != T_NAMESPACE) {
                $usages[] = $t[$key+2][1];
              }
            }
          }
        }
        return array_values(array_unique($usages));
      }
    
      public function getUseStatements()
      {
        $tokenUses = array();
        $level = 0;
    
        foreach($this->content as $key => $token) {
    
          // for traits, only first level uses should be captured
          if (is_string($token)) {
            if ($token == '{') {
              $level++;
            }
            if ($token == '}') {
              $level--;
            }
          }
    
          // capture all use statements besides trait-uses in class
          if (!is_string($token) && $token[0] == T_USE && $level == 0) {
            $tokenUses[] = $key;
          }
        }
    
        $useStatements = array();
    
        // get rid of uses in lambda functions
        foreach($tokenUses as $key => $tokenKey) {
          $i                   = $tokenKey;
          $char                = '';
          $useStatements[$key] = '';
    
          while($char != ';') {
            ++$i;
            $char = is_string($this->content[$i]) ? $this->content[$i] : $this->content[$i][1];
    
            if (!is_string($this->content[$i]) && $this->content[$i][0] == T_AS) {
              $useStatements[$key] .= ' AS ';
            } else {
              $useStatements[$key] .= $char;
            }
    
            if ($char == '(') {
              unset($useStatements[$key]);
              break;
            }
          }
        }
    
        $allUses = array();
    
        // get all use statements
        foreach($useStatements as $fullStmt) {
          $fullStmt = rtrim($fullStmt, ';');
          $fullStmt = preg_replace('/^.+ AS /', '', $fullStmt);
          $fullStmt = explode(',', $fullStmt);
    
          foreach($fullStmt as $singleStmt) {
            // $singleStmt only for full qualified use
            $fqUses[] = $singleStmt;
    
            $singleStmt = explode('\\', $singleStmt);
            $allUses[] = array_pop($singleStmt);
          }
        }
        return $allUses;
      }
    
      public function removeTokens($tokenId)
      {
        foreach($this->content as $key => $token) {
          if (isset($token[0]) && $token[0] == $tokenId) {
            unset($this->content[$key]);
          }
        }
        // reindex
        $this->content = array_values($this->content);
      }
    
    }
    
    $unused = new UseStatementSanitzier('class.php');
    
    print_r($unused->getUnused());
    
    /*
    Returns:
    Array
    (
      [0] => NSname
      [1] => NSname1
    )
    */
    

提交回复
热议问题