PHP_CodeSniffer
[ class tree: PHP_CodeSniffer ] [ index: PHP_CodeSniffer ] [ all elements ]

Source for file IncludeSystemSniff.php

Documentation is available at IncludeSystemSniff.php

  1. <?php
  2. /**
  3.  * Ensures that systems, asset types and libs are included before they are used.
  4.  *
  5.  * @author    Greg Sherwood <gsherwood@squiz.net>
  6.  * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
  7.  * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
  8.  */
  9.  
  10. namespace PHP_CodeSniffer\Standards\MySource\Sniffs\Channels;
  11.  
  12. use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
  13. use PHP_CodeSniffer\Files\File;
  14. use PHP_CodeSniffer\Util\Tokens;
  15.  
  16. class IncludeSystemSniff extends AbstractScopeSniff
  17. {
  18.  
  19.     /**
  20.      * A list of classes that don't need to be included.
  21.      *
  22.      * @var string[] 
  23.      */
  24.     private $ignore = array(
  25.                        'self'                      => true,
  26.                        'static'                    => true,
  27.                        'parent'                    => true,
  28.                        'channels'                  => true,
  29.                        'basesystem'                => true,
  30.                        'dal'                       => true,
  31.                        'init'                      => true,
  32.                        'pdo'                       => true,
  33.                        'util'                      => true,
  34.                        'ziparchive'                => true,
  35.                        'phpunit_framework_assert'  => true,
  36.                        'abstractmysourceunittest'  => true,
  37.                        'abstractdatacleanunittest' => true,
  38.                        'exception'                 => true,
  39.                        'abstractwidgetwidgettype'  => true,
  40.                        'domdocument'               => true,
  41.                       );
  42.  
  43.  
  44.     /**
  45.      * Constructs a Squiz_Sniffs_Scope_MethodScopeSniff.
  46.      */
  47.     public function __construct()
  48.     {
  49.         parent::__construct(array(T_FUNCTION), array(T_DOUBLE_COLON, T_EXTENDS), true);
  50.  
  51.     }//end __construct()
  52.  
  53.  
  54.     /**
  55.      * Processes the function tokens within the class.
  56.      *
  57.      * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
  58.      * @param integer                     $stackPtr  The position where the token was found.
  59.      * @param integer                     $currScope The current scope opener token.
  60.      *
  61.      * @return void 
  62.      */
  63.     protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
  64.     {
  65.         $tokens = $phpcsFile->getTokens();
  66.  
  67.         // Determine the name of the class that the static function
  68.         // is being called on.
  69.         $classNameToken = $phpcsFile->findPrevious(
  70.             T_WHITESPACE,
  71.             ($stackPtr - 1),
  72.             null,
  73.             true
  74.         );
  75.  
  76.         // Don't process class names represented by variables as this can be
  77.         // an inexact science.
  78.         if ($tokens[$classNameToken]['code'] === T_VARIABLE) {
  79.             return;
  80.         }
  81.  
  82.         $className = $tokens[$classNameToken]['content'];
  83.         if (isset($this->ignore[strtolower($className)]) === true) {
  84.             return;
  85.         }
  86.  
  87.         $includedClasses = array();
  88.  
  89.         $fileName = strtolower($phpcsFile->getFilename());
  90.         $matches  = array();
  91.         if (preg_match('|/systems/(.*)/([^/]+)?actions.inc$|', $fileName, $matches) !== 0) {
  92.             // This is an actions file, which means we don't
  93.             // have to include the system in which it exists.
  94.             $includedClasses[$matches[2]] = true;
  95.  
  96.             // Or a system it implements.
  97.             $class      = $phpcsFile->getCondition($stackPtr, T_CLASS);
  98.             $implements = $phpcsFile->findNext(T_IMPLEMENTS, $class, ($class + 10));
  99.             if ($implements !== false) {
  100.                 $implementsClass     = $phpcsFile->findNext(T_STRING, $implements);
  101.                 $implementsClassName = strtolower($tokens[$implementsClass]['content']);
  102.                 if (substr($implementsClassName, -7) === 'actions') {
  103.                     $includedClasses[substr($implementsClassName, 0, -7)] = true;
  104.                 }
  105.             }
  106.         }
  107.  
  108.         // Go searching for includeSystem and includeAsset calls within this
  109.         // function, or the inclusion of .inc files, which
  110.         // would be library files.
  111.         for ($i = ($currScope + 1); $i < $stackPtr; $i++) {
  112.             $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
  113.             if ($name !== false) {
  114.                 $includedClasses[$name] = true;
  115.                 // Special case for Widgets cause they are, well, special.
  116.             } else if (strtolower($tokens[$i]['content']) === 'includewidget') {
  117.                 $typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($i + 1));
  118.                 $typeName = trim($tokens[$typeName]['content'], " '");
  119.                 $includedClasses[strtolower($typeName).'widgettype'] = true;
  120.             }
  121.         }
  122.  
  123.         // Now go searching for includeSystem, includeAsset or require/include
  124.         // calls outside our scope. If we are in a class, look outside the
  125.         // class. If we are not, look outside the function.
  126.         $condPtr = $currScope;
  127.         if ($phpcsFile->hasCondition($stackPtr, T_CLASS) === true) {
  128.             foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condType) {
  129.                 if ($condType === T_CLASS) {
  130.                     break;
  131.                 }
  132.             }
  133.         }
  134.  
  135.         for ($i = 0; $i < $condPtr; $i++) {
  136.             // Skip other scopes.
  137.             if (isset($tokens[$i]['scope_closer']) === true) {
  138.                 $i = $tokens[$i]['scope_closer'];
  139.                 continue;
  140.             }
  141.  
  142.             $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
  143.             if ($name !== false) {
  144.                 $includedClasses[$name] = true;
  145.             }
  146.         }
  147.  
  148.         // If we are in a testing class, we might have also included
  149.         // some systems and classes in our setUp() method.
  150.         $setupFunction = null;
  151.         if ($phpcsFile->hasCondition($stackPtr, T_CLASS) === true) {
  152.             foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condType) {
  153.                 if ($condType === T_CLASS) {
  154.                     // Is this is a testing class?
  155.                     $name = $phpcsFile->findNext(T_STRING, $condPtr);
  156.                     $name = $tokens[$name]['content'];
  157.                     if (substr($name, -8) === 'UnitTest') {
  158.                         // Look for a method called setUp().
  159.                         $end      = $tokens[$condPtr]['scope_closer'];
  160.                         $function = $phpcsFile->findNext(T_FUNCTION, ($condPtr + 1), $end);
  161.                         while ($function !== false) {
  162.                             $name = $phpcsFile->findNext(T_STRING, $function);
  163.                             if ($tokens[$name]['content'] === 'setUp') {
  164.                                 $setupFunction = $function;
  165.                                 break;
  166.                             }
  167.  
  168.                             $function = $phpcsFile->findNext(T_FUNCTION, ($function + 1), $end);
  169.                         }
  170.                     }
  171.                 }
  172.             }//end foreach
  173.         }//end if
  174.  
  175.         if ($setupFunction !== null) {
  176.             $start = ($tokens[$setupFunction]['scope_opener'] + 1);
  177.             $end   = $tokens[$setupFunction]['scope_closer'];
  178.             for ($i = $start; $i < $end; $i++) {
  179.                 $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
  180.                 if ($name !== false) {
  181.                     $includedClasses[$name] = true;
  182.                 }
  183.             }
  184.         }//end if
  185.  
  186.         if (isset($includedClasses[strtolower($className)]) === false) {
  187.             $error = 'Static method called on non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once';
  188.             $data  = array($className);
  189.             $phpcsFile->addError($error, $stackPtr, 'NotIncludedCall', $data);
  190.         }
  191.  
  192.     }//end processTokenWithinScope()
  193.  
  194.  
  195.     /**
  196.      * Processes a token within the scope that this test is listening to.
  197.      *
  198.      * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
  199.      * @param int                         $stackPtr  The position in the stack where
  200.      *                                                this token was found.
  201.      *
  202.      * @return void 
  203.      */
  204.     protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
  205.     {
  206.         $tokens = $phpcsFile->getTokens();
  207.  
  208.         if ($tokens[$stackPtr]['code'] === T_EXTENDS) {
  209.             // Find the class name.
  210.             $classNameToken = $phpcsFile->findNext(T_STRING, ($stackPtr + 1));
  211.             $className      = $tokens[$classNameToken]['content'];
  212.         } else {
  213.             // Determine the name of the class that the static function
  214.             // is being called on. But don't process class names represented by
  215.             // variables as this can be an inexact science.
  216.             $classNameToken = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
  217.             if ($tokens[$classNameToken]['code'] === T_VARIABLE) {
  218.                 return;
  219.             }
  220.  
  221.             $className = $tokens[$classNameToken]['content'];
  222.         }
  223.  
  224.         // Some systems are always available.
  225.         if (isset($this->ignore[strtolower($className)]) === true) {
  226.             return;
  227.         }
  228.  
  229.         $includedClasses = array();
  230.  
  231.         $fileName = strtolower($phpcsFile->getFilename());
  232.         $matches  = array();
  233.         if (preg_match('|/systems/([^/]+)/([^/]+)?actions.inc$|', $fileName, $matches) !== 0) {
  234.             // This is an actions file, which means we don't
  235.             // have to include the system in which it exists
  236.             // We know the system from the path.
  237.             $includedClasses[$matches[1]] = true;
  238.         }
  239.  
  240.         // Go searching for includeSystem, includeAsset or require/include
  241.         // calls outside our scope.
  242.         for ($i = 0; $i < $stackPtr; $i++) {
  243.             // Skip classes and functions as will we never get
  244.             // into their scopes when including this file, although
  245.             // we have a chance of getting into IF's, WHILE's etc.
  246.             if (($tokens[$i]['code'] === T_CLASS
  247.                 || $tokens[$i]['code'] === T_INTERFACE
  248.                 || $tokens[$i]['code'] === T_FUNCTION)
  249.                 && isset($tokens[$i]['scope_closer']) === true
  250.             ) {
  251.                 $i = $tokens[$i]['scope_closer'];
  252.                 continue;
  253.             }
  254.  
  255.             $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
  256.             if ($name !== false) {
  257.                 $includedClasses[$name] = true;
  258.                 // Special case for Widgets cause they are, well, special.
  259.             } else if (strtolower($tokens[$i]['content']) === 'includewidget') {
  260.                 $typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($i + 1));
  261.                 $typeName = trim($tokens[$typeName]['content'], " '");
  262.                 $includedClasses[strtolower($typeName).'widgettype'] = true;
  263.             }
  264.         }//end for
  265.  
  266.         if (isset($includedClasses[strtolower($className)]) === false) {
  267.             if ($tokens[$stackPtr]['code'] === T_EXTENDS) {
  268.                 $error = 'Class extends non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once';
  269.                 $data  = array($className);
  270.                 $phpcsFile->addError($error, $stackPtr, 'NotIncludedExtends', $data);
  271.             } else {
  272.                 $error = 'Static method called on non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once';
  273.                 $data  = array($className);
  274.                 $phpcsFile->addError($error, $stackPtr, 'NotIncludedCall', $data);
  275.             }
  276.         }
  277.  
  278.     }//end processTokenOutsideScope()
  279.  
  280.  
  281.     /**
  282.      * Determines the included class name from given token.
  283.      *
  284.      * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
  285.      * @param array                       $tokens    The array of file tokens.
  286.      * @param int                         $stackPtr  The position in the tokens array of the
  287.      *                                                potentially included class.
  288.      *
  289.      * @return string 
  290.      */
  291.     protected function getIncludedClassFromToken(File $phpcsFile, array $tokens, $stackPtr)
  292.     {
  293.         if (strtolower($tokens[$stackPtr]['content']) === 'includesystem') {
  294.             $systemName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($stackPtr + 1));
  295.             $systemName = trim($tokens[$systemName]['content'], " '");
  296.             return strtolower($systemName);
  297.         } else if (strtolower($tokens[$stackPtr]['content']) === 'includeasset') {
  298.             $typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($stackPtr + 1));
  299.             $typeName = trim($tokens[$typeName]['content'], " '");
  300.             return strtolower($typeName).'assettype';
  301.         } else if (isset(Tokens::$includeTokens[$tokens[$stackPtr]['code']]) === true) {
  302.             $filePath = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($stackPtr + 1));
  303.             $filePath = $tokens[$filePath]['content'];
  304.             $filePath = trim($filePath, " '");
  305.             $filePath = basename($filePath, '.inc');
  306.             return strtolower($filePath);
  307.         }
  308.  
  309.         return false;
  310.  
  311.     }//end getIncludedClassFromToken()
  312.  
  313.  
  314. }//end class

Documentation generated on Mon, 11 Mar 2019 15:27:33 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.