1<?php
2/**
3 * An AbstractScopeTest allows for tests that extend from this class to
4 * listen for tokens within a particular scope.
5 *
6 * PHP version 5
7 *
8 * @category  PHP
9 * @package   PHP_CodeSniffer
10 * @author    Greg Sherwood <gsherwood@squiz.net>
11 * @author    Marc McIntyre <mmcintyre@squiz.net>
12 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
13 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
14 * @link      http://pear.php.net/package/PHP_CodeSniffer
15 */
16
17/**
18 * An AbstractScopeTest allows for tests that extend from this class to
19 * listen for tokens within a particular scope.
20 *
21 * Below is a test that listens to methods that exist only within classes:
22 * <code>
23 * class ClassScopeTest extends PHP_CodeSniffer_Standards_AbstractScopeSniff
24 * {
25 *     public function __construct()
26 *     {
27 *         parent::__construct(array(T_CLASS), array(T_FUNCTION));
28 *     }
29 *
30 *     protected function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $)
31 *     {
32 *         $className = $phpcsFile->getDeclarationName($currScope);
33 *         echo 'encountered a method within class '.$className;
34 *     }
35 * }
36 * </code>
37 *
38 * @category  PHP
39 * @package   PHP_CodeSniffer
40 * @author    Greg Sherwood <gsherwood@squiz.net>
41 * @author    Marc McIntyre <mmcintyre@squiz.net>
42 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
43 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
44 * @version   Release: @package_version@
45 * @link      http://pear.php.net/package/PHP_CodeSniffer
46 */
47abstract class PHP_CodeSniffer_Standards_AbstractScopeSniff implements PHP_CodeSniffer_Sniff
48{
49
50    /**
51     * The token types that this test wishes to listen to within the scope.
52     *
53     * @var array
54     */
55    private $_tokens = array();
56
57    /**
58     * The type of scope opener tokens that this test wishes to listen to.
59     *
60     * @var string
61     */
62    private $_scopeTokens = array();
63
64    /**
65     * True if this test should fire on tokens outside of the scope.
66     *
67     * @var boolean
68     */
69    private $_listenOutside = false;
70
71
72    /**
73     * Constructs a new AbstractScopeTest.
74     *
75     * @param array   $scopeTokens   The type of scope the test wishes to listen to.
76     * @param array   $tokens        The tokens that the test wishes to listen to
77     *                               within the scope.
78     * @param boolean $listenOutside If true this test will also alert the
79     *                               extending class when a token is found outside
80     *                               the scope, by calling the
81     *                               processTokenOutsideScope method.
82     *
83     * @see    PHP_CodeSniffer.getValidScopeTokeners()
84     * @throws PHP_CodeSniffer_Exception If the specified tokens array is empty.
85     */
86    public function __construct(
87        array $scopeTokens,
88        array $tokens,
89        $listenOutside=false
90    ) {
91        if (empty($scopeTokens) === true) {
92            $error = 'The scope tokens list cannot be empty';
93            throw new PHP_CodeSniffer_Exception($error);
94        }
95
96        if (empty($tokens) === true) {
97            $error = 'The tokens list cannot be empty';
98            throw new PHP_CodeSniffer_Exception($error);
99        }
100
101        $invalidScopeTokens = array_intersect($scopeTokens, $tokens);
102        if (empty($invalidScopeTokens) === false) {
103            $invalid = implode(', ', $invalidScopeTokens);
104            $error   = "Scope tokens [$invalid] can't be in the tokens array";
105            throw new PHP_CodeSniffer_Exception($error);
106        }
107
108        $this->_listenOutside = $listenOutside;
109        $this->_scopeTokens   = array_flip($scopeTokens);
110        $this->_tokens        = $tokens;
111
112    }//end __construct()
113
114
115    /**
116     * The method that is called to register the tokens this test wishes to
117     * listen to.
118     *
119     * DO NOT OVERRIDE THIS METHOD. Use the constructor of this class to register
120     * for the desired tokens and scope.
121     *
122     * @return int[]
123     * @see    __constructor()
124     */
125    public final function register()
126    {
127        return $this->_tokens;
128
129    }//end register()
130
131
132    /**
133     * Processes the tokens that this test is listening for.
134     *
135     * @param PHP_CodeSniffer_File $phpcsFile The file where this token was found.
136     * @param int                  $stackPtr  The position in the stack where this
137     *                                        token was found.
138     *
139     * @return void
140     * @see    processTokenWithinScope()
141     */
142    public final function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
143    {
144        $tokens = $phpcsFile->getTokens();
145
146        $foundScope = false;
147        foreach ($tokens[$stackPtr]['conditions'] as $scope => $code) {
148            if (isset($this->_scopeTokens[$code]) === true) {
149                $this->processTokenWithinScope($phpcsFile, $stackPtr, $scope);
150                $foundScope = true;
151            }
152        }
153
154        if ($this->_listenOutside === true && $foundScope === false) {
155            $this->processTokenOutsideScope($phpcsFile, $stackPtr);
156        }
157
158    }//end process()
159
160
161    /**
162     * Processes a token that is found within the scope that this test is
163     * listening to.
164     *
165     * @param PHP_CodeSniffer_File $phpcsFile The file where this token was found.
166     * @param int                  $stackPtr  The position in the stack where this
167     *                                        token was found.
168     * @param int                  $currScope The position in the tokens array that
169     *                                        opened the scope that this test is
170     *                                        listening for.
171     *
172     * @return void
173     */
174    protected abstract function processTokenWithinScope(
175        PHP_CodeSniffer_File $phpcsFile,
176        $stackPtr,
177        $currScope
178    );
179
180
181    /**
182     * Processes a token that is found outside the scope that this test is
183     * listening to.
184     *
185     * @param PHP_CodeSniffer_File $phpcsFile The file where this token was found.
186     * @param int                  $stackPtr  The position in the stack where this
187     *                                        token was found.
188     *
189     * @return void
190     */
191    protected function processTokenOutsideScope(
192        PHP_CodeSniffer_File $phpcsFile,
193        $stackPtr
194    ) {
195
196    }//end processTokenOutsideScope()
197
198
199}//end class
200