1<?php
2/**
3 * Generic_Sniffs_VersionControl_SubversionPropertiesSniff.
4 *
5 * PHP version 5
6 *
7 * @category  PHP
8 * @package   PHP_CodeSniffer
9 * @author    Jack Bates <ms419@freezone.co.uk>
10 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
11 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
12 * @link      http://pear.php.net/package/PHP_CodeSniffer
13 */
14
15/**
16 * Generic_Sniffs_VersionControl_SubversionPropertiesSniff.
17 *
18 * Tests that the correct Subversion properties are set.
19 *
20 * @category  PHP
21 * @package   PHP_CodeSniffer
22 * @author    Jack Bates <ms419@freezone.co.uk>
23 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
24 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
25 * @version   Release: @package_version@
26 * @link      http://pear.php.net/package/PHP_CodeSniffer
27 */
28class Generic_Sniffs_VersionControl_SubversionPropertiesSniff implements PHP_CodeSniffer_Sniff
29{
30
31    /**
32     * The Subversion properties that should be set.
33     *
34     * Key of array is the SVN property and the value is the
35     * exact value the property should have or NULL if the
36     * property should just be set but the value is not fixed.
37     *
38     * @var array
39     */
40    protected $properties = array(
41                             'svn:keywords'  => 'Author Id Revision',
42                             'svn:eol-style' => 'native',
43                            );
44
45
46    /**
47     * Returns an array of tokens this test wants to listen for.
48     *
49     * @return array
50     */
51    public function register()
52    {
53        return array(T_OPEN_TAG);
54
55    }//end register()
56
57
58    /**
59     * Processes this test, when one of its tokens is encountered.
60     *
61     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
62     * @param int                  $stackPtr  The position of the current token
63     *                                        in the stack passed in $tokens.
64     *
65     * @return void
66     */
67    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
68    {
69        $tokens = $phpcsFile->getTokens();
70
71        // Make sure this is the first PHP open tag so we don't process the
72        // same file twice.
73        $prevOpenTag = $phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1));
74        if ($prevOpenTag !== false) {
75            return;
76        }
77
78        $path       = $phpcsFile->getFileName();
79        $properties = $this->getProperties($path);
80        if ($properties === null) {
81            // Not under version control.
82            return;
83        }
84
85        $allProperties = ($properties + $this->properties);
86        foreach ($allProperties as $key => $value) {
87            if (isset($properties[$key]) === true
88                && isset($this->properties[$key]) === false
89            ) {
90                $error = 'Unexpected Subversion property "%s" = "%s"';
91                $data  = array(
92                          $key,
93                          $properties[$key],
94                         );
95                $phpcsFile->addError($error, $stackPtr, 'Unexpected', $data);
96                continue;
97            }
98
99            if (isset($properties[$key]) === false
100                && isset($this->properties[$key]) === true
101            ) {
102                $error = 'Missing Subversion property "%s" = "%s"';
103                $data  = array(
104                          $key,
105                          $this->properties[$key],
106                         );
107                $phpcsFile->addError($error, $stackPtr, 'Missing', $data);
108                continue;
109            }
110
111            if ($properties[$key] !== null
112                && $properties[$key] !== $this->properties[$key]
113            ) {
114                $error = 'Subversion property "%s" = "%s" does not match "%s"';
115                $data  = array(
116                          $key,
117                          $properties[$key],
118                          $this->properties[$key],
119                         );
120                $phpcsFile->addError($error, $stackPtr, 'NoMatch', $data);
121            }
122        }//end foreach
123
124    }//end process()
125
126
127    /**
128     * Returns the Subversion properties which are actually set on a path.
129     *
130     * Returns NULL if the file is not under version control.
131     *
132     * @param string $path The path to return Subversion properties on.
133     *
134     * @return array
135     * @throws PHP_CodeSniffer_Exception If Subversion properties file could
136     *                                   not be opened.
137     */
138    protected function getProperties($path)
139    {
140        $properties = array();
141
142        $paths   = array();
143        $paths[] = dirname($path).'/.svn/props/'.basename($path).'.svn-work';
144        $paths[] = dirname($path).'/.svn/prop-base/'.basename($path).'.svn-base';
145
146        $foundPath = false;
147        foreach ($paths as $path) {
148            if (file_exists($path) === true) {
149                $foundPath = true;
150
151                $handle = fopen($path, 'r');
152                if ($handle === false) {
153                    $error = 'Error opening file; could not get Subversion properties';
154                    throw new PHP_CodeSniffer_Exception($error);
155                }
156
157                while (feof($handle) === false) {
158                    // Read a key length line. Might be END, though.
159                    $buffer = trim(fgets($handle));
160
161                    // Check for the end of the hash.
162                    if ($buffer === 'END') {
163                        break;
164                    }
165
166                    // Now read that much into a buffer.
167                    $key = fread($handle, substr($buffer, 2));
168
169                    // Suck up extra newline after key data.
170                    fgetc($handle);
171
172                    // Read a value length line.
173                    $buffer = trim(fgets($handle));
174
175                    // Now read that much into a buffer.
176                    $length = substr($buffer, 2);
177                    if ($length === '0') {
178                        // Length of value is ZERO characters, so
179                        // value is actually empty.
180                        $value = '';
181                    } else {
182                        $value = fread($handle, $length);
183                    }
184
185                    // Suck up extra newline after value data.
186                    fgetc($handle);
187
188                    $properties[$key] = $value;
189                }//end while
190
191                fclose($handle);
192            }//end if
193        }//end foreach
194
195        if ($foundPath === false) {
196            return null;
197        }
198
199        return $properties;
200
201    }//end getProperties()
202
203
204}//end class
205