├── Joomla ├── ExampleRulesets │ ├── Joomla-CMS │ │ └── ruleset.xml │ ├── Joomla-Stats │ │ └── ruleset.xml │ └── README.md ├── Sniffs │ ├── Classes │ │ └── InstantiateNewClassesSniff.php │ ├── Commenting │ │ ├── ClassCommentSniff.php │ │ ├── FileCommentSniff.php │ │ ├── FunctionCommentSniff.php │ │ └── SingleCommentSniff.php │ ├── ControlStructures │ │ ├── ControlSignatureSniff.php │ │ ├── ControlStructuresBracketsSniff.php │ │ └── WhiteSpaceBeforeSniff.php │ ├── Functions │ │ └── StatementNotFunctionSniff.php │ ├── NamingConventions │ │ ├── ValidFunctionNameSniff.php │ │ └── ValidVariableNameSniff.php │ ├── Operators │ │ └── ValidLogicalOperatorsSniff.php │ └── WhiteSpace │ │ └── MemberVarSpacingSniff.php └── ruleset.xml ├── LICENSE ├── README.md ├── composer.json ├── manual ├── README.md ├── appendices │ ├── analysis.md │ └── examples.md ├── basic-guidelines.md ├── css.md ├── docblocks.md ├── html.md ├── ini.md ├── inline-comments.md ├── introduction.md ├── javascript-j3.md ├── javascript-j4.md ├── license.md ├── php.md ├── scss.md ├── source-code-management.md ├── version.md └── xml.md └── notes.txt /Joomla/ExampleRulesets/Joomla-Stats/ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | */build/* 12 | */tests/* 13 | */lib/* 14 | */tmpl/* 15 | */layouts/* 16 | 17 | 18 | */libraries/* 19 | */vendor/* 20 | */editors/* 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Joomla/ExampleRulesets/README.md: -------------------------------------------------------------------------------- 1 | ### Selectively Applying Rules 2 | 3 | #### NOTE: this is an exact copy of the "Selectively Applying Rules" section of the main README and is here only for reference 4 | 5 | For consuming packages there are some items that will typically result in creating their own project ruleset.xml, rather than just directly using the Joomla ruleset.xml. A project ruleset.xml allows the coding standard to be selectivly applied for excluding 3rd party libraries, for consideration of backwards compatability in existing projects, or for adjustments necessary for projects that do not need php 5.3 compatability (which will be removed in a future version of the Joomla Coding Standard in connection of the end of php 5.3 support in all active Joomla Projects). 6 | 7 | For information on [selectivly applying rules read details in the PHP CodeSniffer wiki](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml#selectively-applying-rules) 8 | 9 | #### Common Rule Set Adjustments 10 | 11 | The most common adjustment is to exclude folders with 3rd party libraries, or where the code has yet to have coding standards applied. 12 | 13 | ```xml 14 | 15 | build/* 16 | tests/* 17 | 18 | 19 | libraries/* 20 | vendor/* 21 | ``` 22 | 23 | Another common adjustment is to exclude the [camelCase format requirement](http://joomla.github.io/coding-standards/?coding-standards/chapters/php.md) for "Classes, Functions, Methods, Regular Variables and Class Properties" the essentially allows for B/C with snake_case variables which were only allowed in the context of interacting with the database. 24 | 25 | ```xml 26 | 27 | 28 | 29 | 30 | 31 | 32 | ``` 33 | 34 | Old Protected method names were at one time prefixed with an underscore. These Protected method names with underscores are depreceated in Joomla projects but for B\C reasons are still in the projects. As such excluding the MethodUnderscore sniff is a common ruleset adjustment 35 | 36 | ```xml 37 | 38 | 39 | 40 | 41 | ``` 42 | 43 | The last most common adjustment is removing PHP 5.3 specific rules which prevent short array syntax, and allowing short array syntax for method parameters. 44 | 45 | ```xml 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | ``` 56 | -------------------------------------------------------------------------------- /Joomla/Sniffs/Classes/InstantiateNewClassesSniff.php: -------------------------------------------------------------------------------- 1 | getTokens(); 46 | 47 | $running = true; 48 | $valid = false; 49 | $started = false; 50 | 51 | $cnt = $stackPtr + 1; 52 | 53 | do 54 | { 55 | if (false === isset($tokens[$cnt])) 56 | { 57 | $running = false; 58 | } 59 | else 60 | { 61 | switch ($tokens[$cnt]['code']) 62 | { 63 | case T_SEMICOLON : 64 | case T_COMMA : 65 | $valid = true; 66 | $running = false; 67 | break; 68 | 69 | case T_OPEN_PARENTHESIS : 70 | $started = true; 71 | break; 72 | 73 | case T_VARIABLE : 74 | case T_STRING : 75 | case T_LNUMBER : 76 | case T_CONSTANT_ENCAPSED_STRING : 77 | case T_DOUBLE_QUOTED_STRING : 78 | case T_ARRAY : 79 | case T_TRUE : 80 | case T_FALSE : 81 | case T_NULL : 82 | if ($started === true) 83 | { 84 | $valid = true; 85 | $running = false; 86 | } 87 | 88 | break; 89 | 90 | case T_CLOSE_PARENTHESIS : 91 | if ($started === false) 92 | { 93 | $valid = true; 94 | } 95 | 96 | $running = false; 97 | break; 98 | 99 | case T_WHITESPACE : 100 | break; 101 | 102 | case T_OPEN_SHORT_ARRAY : 103 | if ($this->shortArraySyntax === true) 104 | { 105 | if ($started === true) 106 | { 107 | $valid = true; 108 | $running = false; 109 | } 110 | } 111 | break; 112 | } 113 | 114 | $cnt++; 115 | } 116 | } 117 | while ($running === true); 118 | 119 | if ($valid === false) 120 | { 121 | $error = 'Instanciating new class without parameters does not require brackets.'; 122 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NewClass'); 123 | 124 | if ($fix === true) 125 | { 126 | $classNameEnd = $phpcsFile->findNext( 127 | array( 128 | T_VARIABLE, 129 | T_WHITESPACE, 130 | T_NS_SEPARATOR, 131 | T_STRING, 132 | T_SELF, 133 | ), 134 | ($stackPtr + 1), 135 | null, 136 | true, 137 | null, 138 | true 139 | ); 140 | 141 | $phpcsFile->fixer->beginChangeset(); 142 | 143 | if ($tokens[($stackPtr + 3)]['code'] === T_WHITESPACE) 144 | { 145 | $phpcsFile->fixer->replaceToken(($stackPtr + 3), ''); 146 | } 147 | 148 | for ($i = $classNameEnd; $i < $cnt; $i++) 149 | { 150 | $phpcsFile->fixer->replaceToken($i, ''); 151 | } 152 | 153 | $phpcsFile->fixer->endChangeset(); 154 | } 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Joomla/Sniffs/Commenting/ClassCommentSniff.php: -------------------------------------------------------------------------------- 1 | array( 29 | 'required' => false, 30 | 'allow_multiple' => false, 31 | 'order_text' => 'is first', 32 | ), 33 | '@package' => array( 34 | 'required' => false, 35 | 'allow_multiple' => false, 36 | 'order_text' => 'must follow @category (if used)', 37 | ), 38 | '@subpackage' => array( 39 | 'required' => false, 40 | 'allow_multiple' => false, 41 | 'order_text' => 'must follow @package', 42 | ), 43 | '@author' => array( 44 | 'required' => false, 45 | 'allow_multiple' => true, 46 | 'order_text' => 'is first', 47 | ), 48 | '@copyright' => array( 49 | 'required' => false, 50 | 'allow_multiple' => true, 51 | 'order_text' => 'must follow @author (if used) or @subpackage (if used) or @package (if used)', 52 | ), 53 | '@license' => array( 54 | 'required' => false, 55 | 'allow_multiple' => false, 56 | 'order_text' => 'must follow @copyright (if used)', 57 | ), 58 | '@link' => array( 59 | 'required' => false, 60 | 'allow_multiple' => true, 61 | 'order_text' => 'must follow @license (if used)', 62 | ), 63 | '@see' => array( 64 | 'required' => false, 65 | 'allow_multiple' => true, 66 | 'order_text' => 'must follow @link (if used)', 67 | ), 68 | '@since' => array( 69 | 'required' => true, 70 | 'allow_multiple' => false, 71 | 'order_text' => 'must follow @see (if used) or @link (if used)', 72 | ), 73 | '@deprecated' => array( 74 | 'required' => false, 75 | 'allow_multiple' => false, 76 | 'order_text' => 'must follow @since (if used) or @see (if used) or @link (if used)', 77 | ), 78 | ); 79 | 80 | /** 81 | * Returns an array of tokens this test wants to listen for. 82 | * 83 | * @return array 84 | */ 85 | public function register() 86 | { 87 | return array( 88 | T_CLASS, 89 | T_INTERFACE, 90 | T_TRAIT 91 | ); 92 | } 93 | 94 | /** 95 | * Processes this test, when one of its tokens is encountered. 96 | * 97 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 98 | * @param int $stackPtr The position of the current token 99 | * in the stack passed in $tokens. 100 | * 101 | * @return void 102 | */ 103 | public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 104 | { 105 | $this->currentFile = $phpcsFile; 106 | $tokens = $phpcsFile->getTokens(); 107 | $type = strtolower($tokens[$stackPtr]['content']); 108 | $errorData = array($type); 109 | $find = PHP_CodeSniffer_Tokens::$methodPrefixes; 110 | $find[] = T_WHITESPACE; 111 | $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true); 112 | 113 | if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG 114 | && $tokens[$commentEnd]['code'] !== T_COMMENT 115 | ) 116 | { 117 | $phpcsFile->addError('Missing class doc comment', $stackPtr, 'Missing'); 118 | $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'no'); 119 | 120 | return; 121 | } 122 | 123 | $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'yes'); 124 | 125 | if ($tokens[$commentEnd]['code'] === T_COMMENT) 126 | { 127 | $phpcsFile->addError('You must use "/**" style comments for a class comment', $stackPtr, 'WrongStyle'); 128 | 129 | return; 130 | } 131 | 132 | // Check each tag. 133 | $this->processTags($phpcsFile, $stackPtr, $tokens[$commentEnd]['comment_opener']); 134 | }//end process() 135 | } 136 | -------------------------------------------------------------------------------- /Joomla/Sniffs/Commenting/FileCommentSniff.php: -------------------------------------------------------------------------------- 1 | 16 | * @author Marc McIntyre 17 | * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 18 | * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 19 | * @link http://pear.php.net/package/PHP_CodeSniffer 20 | * 21 | * @since 1.0 22 | */ 23 | class Joomla_Sniffs_Commenting_FileCommentSniff implements PHP_CodeSniffer_Sniff 24 | { 25 | /** 26 | * Tags in correct order and related info. 27 | * 28 | * @var array 29 | */ 30 | protected $tags = array( 31 | '@version' => array( 32 | 'required' => false, 33 | 'allow_multiple' => false, 34 | 'order_text' => 'must be first', 35 | ), 36 | '@category' => array( 37 | 'required' => false, 38 | 'allow_multiple' => false, 39 | 'order_text' => 'precedes @package', 40 | ), 41 | '@package' => array( 42 | 'required' => false, 43 | 'allow_multiple' => false, 44 | 'order_text' => 'must follows @category (if used)', 45 | ), 46 | '@subpackage' => array( 47 | 'required' => false, 48 | 'allow_multiple' => false, 49 | 'order_text' => 'must follow @package', 50 | ), 51 | '@author' => array( 52 | 'required' => false, 53 | 'allow_multiple' => true, 54 | 'order_text' => 'must follow @subpackage (if used) or @package', 55 | ), 56 | '@copyright' => array( 57 | 'required' => true, 58 | 'allow_multiple' => true, 59 | 'order_text' => 'must follow @author (if used), @subpackage (if used) or @package', 60 | ), 61 | '@license' => array( 62 | 'required' => true, 63 | 'allow_multiple' => false, 64 | 'order_text' => 'must follow @copyright', 65 | ), 66 | '@link' => array( 67 | 'required' => false, 68 | 'allow_multiple' => true, 69 | 'order_text' => 'must follow @license', 70 | ), 71 | '@see' => array( 72 | 'required' => false, 73 | 'allow_multiple' => true, 74 | 'order_text' => 'must follow @link (if used) or @license', 75 | ), 76 | '@since' => array( 77 | 'required' => false, 78 | 'allow_multiple' => false, 79 | 'order_text' => 'must follows @see (if used), @link (if used) or @license', 80 | ), 81 | '@deprecated' => array( 82 | 'required' => false, 83 | 'allow_multiple' => false, 84 | 'order_text' => 'must follow @since (if used), @see (if used), @link (if used) or @license', 85 | ), 86 | ); 87 | 88 | /** 89 | * A list of tokenizers this sniff supports. 90 | * 91 | * @var array 92 | */ 93 | public $supportedTokenizers = array( 94 | 'PHP', 95 | 'JS', 96 | ); 97 | 98 | /** 99 | * The header comment parser for the current file. 100 | * 101 | * @var PHP_CodeSniffer_Comment_Parser_ClassCommentParser 102 | */ 103 | protected $commentParser = null; 104 | 105 | /** 106 | * The current PHP_CodeSniffer_File object we are processing. 107 | * 108 | * @var PHP_CodeSniffer_File 109 | */ 110 | protected $currentFile = null; 111 | 112 | /** 113 | * Returns an array of tokens this test wants to listen for. 114 | * 115 | * @return array 116 | */ 117 | public function register() 118 | { 119 | return array(T_OPEN_TAG); 120 | }//end register() 121 | 122 | /** 123 | * Processes this test, when one of its tokens is encountered. 124 | * 125 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 126 | * @param int $stackPtr The position of the current token in the stack passed in $tokens. 127 | * 128 | * @return integer 129 | */ 130 | public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 131 | { 132 | $tokens = $phpcsFile->getTokens(); 133 | 134 | // Find the next non whitespace token. 135 | $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); 136 | 137 | // Allow declare() statements at the top of the file. 138 | if ($tokens[$commentStart]['code'] === T_DECLARE) 139 | { 140 | $semicolon = $phpcsFile->findNext(T_SEMICOLON, ($commentStart + 1)); 141 | $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($semicolon + 1), null, true); 142 | } 143 | 144 | // Ignore vim header. 145 | if ($tokens[$commentStart]['code'] === T_COMMENT) 146 | { 147 | if (strstr($tokens[$commentStart]['content'], 'vim:') !== false) 148 | { 149 | $commentStart = $phpcsFile->findNext( 150 | T_WHITESPACE, 151 | ($commentStart + 1), 152 | null, 153 | true 154 | ); 155 | } 156 | } 157 | 158 | $errorToken = ($stackPtr + 1); 159 | 160 | if (isset($tokens[$errorToken]) === false) 161 | { 162 | $errorToken--; 163 | } 164 | 165 | if ($tokens[$commentStart]['code'] === T_CLOSE_TAG) 166 | { 167 | // We are only interested if this is the first open tag. 168 | return ($phpcsFile->numTokens + 1); 169 | } 170 | elseif ($tokens[$commentStart]['code'] === T_COMMENT) 171 | { 172 | $error = 'You must use "/**" style comments for a file comment'; 173 | $phpcsFile->addError($error, $errorToken, 'WrongStyle'); 174 | $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes'); 175 | 176 | return ($phpcsFile->numTokens + 1); 177 | } 178 | elseif ($commentStart === false 179 | || $tokens[$commentStart]['code'] !== T_DOC_COMMENT_OPEN_TAG 180 | ) 181 | { 182 | $phpcsFile->addError('Missing file doc comment', $errorToken, 'Missing'); 183 | $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'no'); 184 | 185 | return ($phpcsFile->numTokens + 1); 186 | } 187 | else 188 | { 189 | $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes'); 190 | } 191 | 192 | // Check each tag. 193 | $this->processTags($phpcsFile, $stackPtr, $commentStart); 194 | 195 | // Ignore the rest of the file. 196 | return ($phpcsFile->numTokens + 1); 197 | }//end process() 198 | 199 | /** 200 | * Processes each required or optional tag. 201 | * 202 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 203 | * @param int $stackPtr The position of the current token 204 | * in the stack passed in $tokens. 205 | * @param int $commentStart Position in the stack where the comment started. 206 | * 207 | * @return void 208 | */ 209 | protected function processTags(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $commentStart) 210 | { 211 | $tokens = $phpcsFile->getTokens(); 212 | 213 | if (get_class($this) === 'Joomla_Sniffs_Commenting_FileCommentSniff') 214 | { 215 | $docBlock = 'file'; 216 | } 217 | else 218 | { 219 | $docBlock = 'class'; 220 | } 221 | 222 | $commentEnd = $tokens[$commentStart]['comment_closer']; 223 | $foundTags = array(); 224 | $tagTokens = array(); 225 | 226 | foreach ($tokens[$commentStart]['comment_tags'] as $tag) 227 | { 228 | $name = $tokens[$tag]['content']; 229 | 230 | if (isset($this->tags[$name]) === false) 231 | { 232 | continue; 233 | } 234 | 235 | if ($this->tags[$name]['allow_multiple'] === false && isset($tagTokens[$name]) === true) 236 | { 237 | $error = 'Only one %s tag is allowed in a %s comment'; 238 | $data = array( 239 | $name, 240 | $docBlock, 241 | ); 242 | $phpcsFile->addError($error, $tag, 'Duplicate' . ucfirst(substr($name, 1)) . 'Tag', $data); 243 | } 244 | 245 | $foundTags[] = $name; 246 | $tagTokens[$name][] = $tag; 247 | $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd); 248 | 249 | if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) 250 | { 251 | $error = 'Content missing for %s tag in %s comment'; 252 | $data = array( 253 | $name, 254 | $docBlock, 255 | ); 256 | $phpcsFile->addError($error, $tag, 'Empty' . ucfirst(substr($name, 1)) . 'Tag', $data); 257 | continue; 258 | } 259 | }//end foreach 260 | 261 | // Check if the tags are in the correct position. 262 | $pos = 0; 263 | 264 | foreach ($this->tags as $tag => $tagData) 265 | { 266 | // We don't use package tags in namespaced classes. 267 | if ($tag === '@package' || $tag === '@subpackage') 268 | { 269 | // Check for a namespace token, if certain other tokens are found we can move on. This keeps us from searching the whole file. 270 | $namespaced = $phpcsFile->findNext(array(T_NAMESPACE, T_CLASS, T_INTERFACE, T_TRAIT), 0); 271 | $classes = array(T_CLASS, T_INTERFACE, T_TRAIT); 272 | $class = $phpcsFile->findNext($classes, 0); 273 | 274 | // If we found a namespaced class trigger the error. 275 | if ($tokens[$namespaced]['code'] === T_NAMESPACE && in_array($tokens[$class]['code'], $classes, true)) 276 | { 277 | if (isset($tagTokens[$tag]) === true) 278 | { 279 | $error = '%s tag found in namespaced %s comment'; 280 | $data = array( 281 | $tag, 282 | $docBlock, 283 | ); 284 | $phpcsFile->addError($error, $commentEnd, ucfirst(substr($tag, 1)) . 'TagInNamespace', $data); 285 | } 286 | 287 | continue; 288 | } 289 | } 290 | 291 | if (isset($tagTokens[$tag]) === false) 292 | { 293 | if ($tagData['required'] === true) 294 | { 295 | $error = 'Missing %s tag in %s comment'; 296 | $data = array( 297 | $tag, 298 | $docBlock, 299 | ); 300 | $phpcsFile->addError($error, $commentEnd, 'Missing' . ucfirst(substr($tag, 1)) . 'Tag', $data); 301 | } 302 | 303 | continue; 304 | } 305 | else 306 | { 307 | $method = 'process' . substr($tag, 1); 308 | 309 | if (method_exists($this, $method) === true) 310 | { 311 | // Process each tag if a method is defined. 312 | call_user_func(array($this, $method), $phpcsFile, $tagTokens[$tag]); 313 | } 314 | } 315 | 316 | if (isset($foundTags[$pos]) === false) 317 | { 318 | break; 319 | } 320 | 321 | if ($foundTags[$pos] !== $tag) 322 | { 323 | $error = 'The tag in position %s should be the %s tag'; 324 | $data = array( 325 | ($pos + 1), 326 | $tag, 327 | ); 328 | $phpcsFile->addError($error, $tokens[$commentStart]['comment_tags'][$pos], ucfirst(substr($tag, 1)) . 'TagOrder', $data); 329 | } 330 | 331 | // Account for multiple tags. 332 | $pos++; 333 | 334 | while (isset($foundTags[$pos]) === true && $foundTags[$pos] === $tag) 335 | { 336 | $pos++; 337 | } 338 | }//end foreach 339 | }//end processTags() 340 | 341 | /** 342 | * Process the category tag. 343 | * 344 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 345 | * @param array $tags The tokens for these tags. 346 | * 347 | * @return void 348 | */ 349 | protected function processCategory(PHP_CodeSniffer_File $phpcsFile, array $tags) 350 | { 351 | $tokens = $phpcsFile->getTokens(); 352 | 353 | foreach ($tags as $tag) 354 | { 355 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) 356 | { 357 | // No content. 358 | continue; 359 | } 360 | 361 | $content = $tokens[($tag + 2)]['content']; 362 | 363 | if (PHP_CodeSniffer::isUnderscoreName($content) !== true) 364 | { 365 | $newContent = str_replace(' ', '_', $content); 366 | $nameBits = explode('_', $newContent); 367 | $firstBit = array_shift($nameBits); 368 | $newName = ucfirst($firstBit) . '_'; 369 | 370 | foreach ($nameBits as $bit) 371 | { 372 | if ($bit !== '') 373 | { 374 | $newName .= ucfirst($bit) . '_'; 375 | } 376 | } 377 | 378 | $error = 'Category name "%s" is not valid; consider "%s" instead'; 379 | $validName = trim($newName, '_'); 380 | $data = array( 381 | $content, 382 | $validName, 383 | ); 384 | $phpcsFile->addError($error, $tag, 'InvalidCategory', $data); 385 | } 386 | }//end foreach 387 | }//end processCategory() 388 | 389 | /** 390 | * Process the package tag. 391 | * 392 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 393 | * @param array $tags The tokens for these tags. 394 | * 395 | * @return void 396 | */ 397 | protected function processPackage(PHP_CodeSniffer_File $phpcsFile, array $tags) 398 | { 399 | $tokens = $phpcsFile->getTokens(); 400 | 401 | foreach ($tags as $tag) 402 | { 403 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) 404 | { 405 | // No content. 406 | continue; 407 | } 408 | 409 | $content = $tokens[($tag + 2)]['content']; 410 | 411 | if (PHP_CodeSniffer::isUnderscoreName($content) === true) 412 | { 413 | continue; 414 | } 415 | 416 | $newContent = str_replace(' ', '_', $content); 417 | $newContent = trim($newContent, '_'); 418 | $newContent = preg_replace('/[^A-Za-z_]/', '', $newContent); 419 | 420 | if ($newContent === '') 421 | { 422 | $error = 'Package name "%s" is not valid'; 423 | $data = array($content); 424 | $phpcsFile->addError($error, $tag, 'InvalidPackageValue', $data); 425 | } 426 | else 427 | { 428 | $nameBits = explode('_', $newContent); 429 | $firstBit = array_shift($nameBits); 430 | $newName = strtoupper($firstBit[0]) . substr($firstBit, 1) . '_'; 431 | 432 | foreach ($nameBits as $bit) 433 | { 434 | if ($bit !== '') 435 | { 436 | $newName .= strtoupper($bit[0]) . substr($bit, 1) . '_'; 437 | } 438 | } 439 | 440 | $error = 'Package name "%s" is not valid; consider "%s" instead'; 441 | $validName = trim($newName, '_'); 442 | $data = array( 443 | $content, 444 | $validName, 445 | ); 446 | $phpcsFile->addError($error, $tag, 'InvalidPackage', $data); 447 | }//end if 448 | }//end foreach 449 | }//end processPackage() 450 | 451 | /** 452 | * Process the subpackage tag. 453 | * 454 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 455 | * @param array $tags The tokens for these tags. 456 | * 457 | * @return void 458 | */ 459 | protected function processSubpackage(PHP_CodeSniffer_File $phpcsFile, array $tags) 460 | { 461 | $tokens = $phpcsFile->getTokens(); 462 | 463 | foreach ($tags as $tag) 464 | { 465 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) 466 | { 467 | // No content. 468 | continue; 469 | } 470 | 471 | $content = $tokens[($tag + 2)]['content']; 472 | 473 | // Is the subpackage included and empty. 474 | if (empty($content) || $content == '') 475 | { 476 | $error = 'if included, @subpackage tag must contain a name'; 477 | $phpcsFile->addError($error, $tag, 'EmptySubpackage'); 478 | } 479 | }//end foreach 480 | }//end processSubpackage() 481 | 482 | /** 483 | * Process the author tag(s) that this header comment has. 484 | * 485 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 486 | * @param array $tags The tokens for these tags. 487 | * 488 | * @return void 489 | */ 490 | protected function processAuthor(PHP_CodeSniffer_File $phpcsFile, array $tags) 491 | { 492 | $tokens = $phpcsFile->getTokens(); 493 | 494 | foreach ($tags as $tag) 495 | { 496 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) 497 | { 498 | // No content. 499 | continue; 500 | } 501 | 502 | $content = $tokens[($tag + 2)]['content']; 503 | $local = '\da-zA-Z-_+'; 504 | 505 | // Dot character cannot be the first or last character in the local-part. 506 | $localMiddle = $local . '.\w'; 507 | 508 | if (preg_match( 509 | '/^([^<]*)\s+<([' . $local . ']([' . $localMiddle . ']*[' . $local . '])*@[\da-zA-Z][-.\w]*[\da-zA-Z]\.[a-zA-Z]{2,7})>$/', 510 | $content 511 | ) === 0) 512 | { 513 | $error = 'Content of the @author tag must be in the form "Display Name "'; 514 | $phpcsFile->addError($error, $tag, 'InvalidAuthors'); 515 | } 516 | } 517 | }//end processAuthor() 518 | 519 | /** 520 | * Process the copyright tags. 521 | * 522 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 523 | * @param array $tags The tokens for these tags. 524 | * 525 | * @return void 526 | */ 527 | protected function processCopyright(PHP_CodeSniffer_File $phpcsFile, array $tags) 528 | { 529 | $tokens = $phpcsFile->getTokens(); 530 | 531 | foreach ($tags as $tag) 532 | { 533 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) 534 | { 535 | // No content. 536 | continue; 537 | } 538 | 539 | $content = $tokens[($tag + 2)]['content']; 540 | $matches = array(); 541 | 542 | if (preg_match('/^.*?([0-9]{4})((.{1})([0-9]{4}))? (.+)$/', $content, $matches) !== 0) 543 | { 544 | // Check earliest-latest year order. 545 | if ($matches[3] !== '' && $matches[3] !== null) 546 | { 547 | if ($matches[3] !== '-') 548 | { 549 | $error = 'A hyphen must be used between the earliest and latest year'; 550 | $phpcsFile->addError($error, $tag, 'CopyrightHyphen'); 551 | } 552 | 553 | if ($matches[4] !== '' && $matches[4] !== null && $matches[4] < $matches[1]) 554 | { 555 | $error = "Invalid year span \"$matches[1]$matches[3]$matches[4]\" found; consider \"$matches[4]-$matches[1]\" instead"; 556 | $phpcsFile->addWarning($error, $tag, 'InvalidCopyright'); 557 | } 558 | } 559 | } 560 | else 561 | { 562 | $error = '@copyright tag must contain a year and the name of the copyright holder'; 563 | $phpcsFile->addError($error, $tag, 'IncompleteCopyright'); 564 | } 565 | }//end foreach 566 | }//end processCopyright() 567 | 568 | /** 569 | * Process the license tag. 570 | * 571 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 572 | * @param array $tags The tokens for these tags. 573 | * 574 | * @return void 575 | */ 576 | protected function processLicense(PHP_CodeSniffer_File $phpcsFile, array $tags) 577 | { 578 | $tokens = $phpcsFile->getTokens(); 579 | 580 | foreach ($tags as $tag) 581 | { 582 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) 583 | { 584 | // No content. 585 | continue; 586 | } 587 | 588 | $content = $tokens[($tag + 2)]['content']; 589 | $matches = array(); 590 | preg_match('/^([^\s]+)\s+(.*)/', $content, $matches); 591 | 592 | if (count($matches) !== 3) 593 | { 594 | $error = '@license tag must contain a URL and a license name'; 595 | $phpcsFile->addError($error, $tag, 'IncompleteLicense'); 596 | } 597 | } 598 | }//end processLicense() 599 | 600 | /** 601 | * Process the version tag. 602 | * 603 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 604 | * @param array $tags The tokens for these tags. 605 | * 606 | * @return void 607 | */ 608 | protected function processVersion(PHP_CodeSniffer_File $phpcsFile, array $tags) 609 | { 610 | $tokens = $phpcsFile->getTokens(); 611 | 612 | foreach ($tags as $tag) 613 | { 614 | $content = $tokens[($tag)]['code']; 615 | 616 | if ($content === '@version') 617 | { 618 | $error = '@version tag in file comment in not required; consider removing or using @since'; 619 | $data = array($content); 620 | $phpcsFile->addWarning($error, $tag, 'IncludedVersion', $data); 621 | } 622 | } 623 | }//end processVersion() 624 | }//end class 625 | -------------------------------------------------------------------------------- /Joomla/Sniffs/Commenting/FunctionCommentSniff.php: -------------------------------------------------------------------------------- 1 | getTokens(); 39 | 40 | $methodName = $phpcsFile->getDeclarationName($stackPtr); 41 | $isSpecialMethod = ($methodName === '__construct' || $methodName === '__destruct'); 42 | 43 | $return = null; 44 | 45 | foreach ($tokens[$commentStart]['comment_tags'] as $tag) 46 | { 47 | if ($tokens[$tag]['content'] === '@return') 48 | { 49 | // Joomla Standard - Constructors and destructors should not have a @return tag 50 | if ($isSpecialMethod) 51 | { 52 | $error = 'Constructor and destructor comments must not have a @return tag'; 53 | $phpcsFile->addError($error, $tag, 'UselessReturn'); 54 | 55 | return; 56 | } 57 | elseif ($return !== null) 58 | { 59 | $error = 'Only 1 @return tag is allowed in a function comment'; 60 | $phpcsFile->addError($error, $tag, 'DuplicateReturn'); 61 | 62 | return; 63 | } 64 | 65 | $return = $tag; 66 | } 67 | } 68 | 69 | if ($isSpecialMethod === true) 70 | { 71 | return; 72 | } 73 | 74 | if ($return !== null) 75 | { 76 | $content = $tokens[($return + 2)]['content']; 77 | 78 | if (empty($content) === true || $tokens[($return + 2)]['code'] !== T_DOC_COMMENT_STRING) 79 | { 80 | $error = 'Return type missing for @return tag in function comment'; 81 | $phpcsFile->addError($error, $return, 'MissingReturnType'); 82 | } 83 | else 84 | { 85 | // Support both a return type and a description. 86 | preg_match('`^((?:\|?(?:array\([^\)]*\)|[\\\\a-z0-9\[\]]+))*)( .*)?`i', $content, $returnParts); 87 | 88 | if (isset($returnParts[1]) === false) 89 | { 90 | return; 91 | } 92 | 93 | $returnType = $returnParts[1]; 94 | 95 | // Check return type (can have multiple return types separated by '|'). 96 | $typeNames = explode('|', $returnType); 97 | $suggestedNames = array(); 98 | 99 | foreach ($typeNames as $i => $typeName) 100 | { 101 | $suggestedName = PHP_CodeSniffer::suggestType($typeName); 102 | 103 | if (in_array($suggestedName, $suggestedNames) === false) 104 | { 105 | $suggestedNames[] = $suggestedName; 106 | } 107 | } 108 | 109 | $suggestedType = implode('|', $suggestedNames); 110 | 111 | if ($returnType !== $suggestedType) 112 | { 113 | $error = 'Expected "%s" but found "%s" for function return type'; 114 | $data = array( 115 | $suggestedType, 116 | $returnType 117 | ); 118 | 119 | $fix = $phpcsFile->addFixableError($error, $return, 'InvalidReturn', $data); 120 | 121 | if ($fix === true) 122 | { 123 | $replacement = $suggestedType; 124 | 125 | if (empty($returnParts[2]) === false) 126 | { 127 | $replacement .= $returnParts[2]; 128 | } 129 | 130 | $phpcsFile->fixer->replaceToken(($return + 2), $replacement); 131 | unset($replacement); 132 | } 133 | } 134 | 135 | /* 136 | * If return type is not void, there needs to be a return statement 137 | * somewhere in the function that returns something. 138 | * Skip this check for mixed return types. 139 | */ 140 | if ($returnType === 'void') 141 | { 142 | if (isset($tokens[$stackPtr]['scope_closer']) === true) 143 | { 144 | $endToken = $tokens[$stackPtr]['scope_closer']; 145 | 146 | for ($returnToken = $stackPtr; $returnToken < $endToken; $returnToken++) 147 | { 148 | if ($tokens[$returnToken]['code'] === T_CLOSURE 149 | || $tokens[$returnToken]['code'] === T_ANON_CLASS 150 | ) 151 | { 152 | $returnToken = $tokens[$returnToken]['scope_closer']; 153 | continue; 154 | } 155 | 156 | if ($tokens[$returnToken]['code'] === T_RETURN 157 | || $tokens[$returnToken]['code'] === T_YIELD 158 | || $tokens[$returnToken]['code'] === T_YIELD_FROM 159 | ) 160 | { 161 | break; 162 | } 163 | } 164 | 165 | if ($returnToken !== $endToken) 166 | { 167 | // If the function is not returning anything, just exiting, then there is no problem. 168 | $semicolon = $phpcsFile->findNext(T_WHITESPACE, ($returnToken + 1), null, true); 169 | 170 | if ($tokens[$semicolon]['code'] !== T_SEMICOLON) 171 | { 172 | $error = 'Function return type is void, but function contains return statement'; 173 | $phpcsFile->addError($error, $return, 'InvalidReturnVoid'); 174 | } 175 | } 176 | }//end if 177 | } 178 | elseif ($returnType !== 'mixed' && in_array('void', $typeNames, true) === false) 179 | { 180 | // If return type is not void, there needs to be a return statement somewhere in the function that returns something. 181 | if (isset($tokens[$stackPtr]['scope_closer']) === true) 182 | { 183 | $endToken = $tokens[$stackPtr]['scope_closer']; 184 | 185 | for ($returnToken = $stackPtr; $returnToken < $endToken; $returnToken++) 186 | { 187 | if ($tokens[$returnToken]['code'] === T_CLOSURE 188 | || $tokens[$returnToken]['code'] === T_ANON_CLASS 189 | ) 190 | { 191 | $returnToken = $tokens[$returnToken]['scope_closer']; 192 | continue; 193 | } 194 | 195 | if ($tokens[$returnToken]['code'] === T_RETURN 196 | || $tokens[$returnToken]['code'] === T_YIELD 197 | || $tokens[$returnToken]['code'] === T_YIELD_FROM 198 | ) 199 | { 200 | break; 201 | } 202 | } 203 | 204 | if ($returnToken === $endToken) 205 | { 206 | $error = 'Function return type is not void, but function has no return statement'; 207 | $phpcsFile->addError($error, $return, 'InvalidNoReturn'); 208 | } 209 | else 210 | { 211 | $semicolon = $phpcsFile->findNext(T_WHITESPACE, ($returnToken + 1), null, true); 212 | 213 | if ($tokens[$semicolon]['code'] === T_SEMICOLON) 214 | { 215 | $error = 'Function return type is not void, but function is returning void here'; 216 | $phpcsFile->addError($error, $returnToken, 'InvalidReturnNotVoid'); 217 | } 218 | } 219 | } 220 | } 221 | } 222 | } 223 | else 224 | { 225 | $error = 'Missing @return tag in function comment'; 226 | $phpcsFile->addError($error, $tokens[$commentStart]['comment_closer'], 'MissingReturn'); 227 | } 228 | } 229 | 230 | /** 231 | * Process the function parameter comments. 232 | * 233 | * Extends PEAR.Commenting.FunctionComment.processReturn to enforce correct alignment of the doc block. 234 | * 235 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 236 | * @param integer $stackPtr The position of the current token in the stack passed in $tokens. 237 | * @param integer $commentStart The position in the stack where the comment started. 238 | * 239 | * @return void 240 | * 241 | * @todo Reinstate the check that params come after the function's comment and has a blank line before them 242 | * @todo Reinstate the check that there is a blank line after all params are declared 243 | */ 244 | protected function processParams(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $commentStart) 245 | { 246 | $tokens = $phpcsFile->getTokens(); 247 | 248 | $params = array(); 249 | $maxType = 0; 250 | $maxVar = 0; 251 | 252 | foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) 253 | { 254 | if ($tokens[$tag]['content'] !== '@param') 255 | { 256 | continue; 257 | } 258 | 259 | $type = ''; 260 | $typeSpace = 0; 261 | $var = ''; 262 | $varSpace = 0; 263 | $comment = ''; 264 | $commentEnd = 0; 265 | 266 | if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) 267 | { 268 | $matches = array(); 269 | preg_match('/([^$&.]+)(?:((?:\.\.\.)?(?:\$|&)[^\s]+)(?:(\s+)(.*))?)?/', $tokens[($tag + 2)]['content'], $matches); 270 | 271 | if (empty($matches) === false) 272 | { 273 | $typeLen = strlen($matches[1]); 274 | $type = trim($matches[1]); 275 | $typeSpace = ($typeLen - strlen($type)); 276 | $typeLen = strlen($type); 277 | 278 | if ($typeLen > $maxType) 279 | { 280 | $maxType = $typeLen; 281 | } 282 | } 283 | 284 | if (isset($matches[2]) === true) 285 | { 286 | $var = $matches[2]; 287 | $varLen = strlen($var); 288 | 289 | if ($varLen > $maxVar) 290 | { 291 | $maxVar = $varLen; 292 | } 293 | 294 | if (isset($matches[4]) === true) 295 | { 296 | $varSpace = strlen($matches[3]); 297 | $comment = $matches[4]; 298 | 299 | // Any strings until the next tag belong to this comment. 300 | if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) 301 | { 302 | $end = $tokens[$commentStart]['comment_tags'][($pos + 1)]; 303 | } 304 | else 305 | { 306 | $end = $tokens[$commentStart]['comment_closer']; 307 | } 308 | 309 | for ($i = ($tag + 3); $i < $end; $i++) 310 | { 311 | if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) 312 | { 313 | $comment .= ' ' . $tokens[$i]['content']; 314 | $commentEnd = $i; 315 | } 316 | } 317 | } 318 | else 319 | { 320 | $error = 'Missing parameter comment'; 321 | $phpcsFile->addError($error, $tag, 'MissingParamComment'); 322 | } 323 | } 324 | else 325 | { 326 | $error = 'Missing parameter name'; 327 | $phpcsFile->addError($error, $tag, 'MissingParamName'); 328 | } 329 | } 330 | else 331 | { 332 | $error = 'Missing parameter type'; 333 | $phpcsFile->addError($error, $tag, 'MissingParamType'); 334 | } 335 | 336 | $params[] = array( 337 | 'tag' => $tag, 338 | 'type' => $type, 339 | 'var' => $var, 340 | 'comment' => $comment, 341 | 'comment_end' => $commentEnd, 342 | 'type_space' => $typeSpace, 343 | 'var_space' => $varSpace, 344 | 'align_space' => $tokens[($tag + 1)]['content'] 345 | ); 346 | } 347 | 348 | $realParams = $phpcsFile->getMethodParameters($stackPtr); 349 | $foundParams = array(); 350 | $previousParam = null; 351 | 352 | /* 353 | * We want to use ... for all variable length arguments, 354 | * so added this prefix to the variable name so comparisons are easier. 355 | */ 356 | foreach ($realParams as $pos => $param) 357 | { 358 | if ($param['variable_length'] === true) 359 | { 360 | $realParams[$pos]['name'] = '...' . $realParams[$pos]['name']; 361 | } 362 | } 363 | 364 | foreach ($params as $pos => $param) 365 | { 366 | if ($param['var'] === '') 367 | { 368 | continue; 369 | } 370 | 371 | $foundParams[] = $param['var']; 372 | 373 | // Joomla change: There must be 3 spaces after the @param tag to make it line up with the @return tag 374 | if ($param['align_space'] !== ' ') 375 | { 376 | $error = 'Expected 3 spaces before variable type, found %s'; 377 | $spaces = strlen($param['align_space']); 378 | $data = array($spaces); 379 | $fix = $phpcsFile->addFixableError($error, $param['tag'], 'BeforeParamType', $data); 380 | 381 | if ($fix === true) 382 | { 383 | $phpcsFile->fixer->beginChangeset(); 384 | 385 | for ($i = 0; $i < $spaces; $i++) 386 | { 387 | $phpcsFile->fixer->replaceToken(($param['tag'] + 1), ''); 388 | } 389 | 390 | $phpcsFile->fixer->addContent($param['tag'], ' '); 391 | $phpcsFile->fixer->endChangeset(); 392 | } 393 | } 394 | 395 | // Make sure the param name is correct. 396 | if (isset($realParams[$pos]) === true) 397 | { 398 | $realName = $realParams[$pos]['name']; 399 | 400 | if ($realName !== $param['var']) 401 | { 402 | $code = 'ParamNameNoMatch'; 403 | $data = array( 404 | $param['var'], 405 | $realName 406 | ); 407 | 408 | $error = 'Doc comment for parameter %s does not match '; 409 | 410 | if (strtolower($param['var']) === strtolower($realName)) 411 | { 412 | $error .= 'case of '; 413 | $code = 'ParamNameNoCaseMatch'; 414 | } 415 | 416 | $error .= 'actual variable name %s'; 417 | 418 | $phpcsFile->addError($error, $param['tag'], $code, $data); 419 | } 420 | } 421 | elseif (substr($param['var'], -4) !== ',...') 422 | { 423 | // We must have an extra parameter comment. 424 | $error = 'Superfluous parameter comment'; 425 | $phpcsFile->addError($error, $param['tag'], 'ExtraParamComment'); 426 | } 427 | 428 | if ($param['comment'] === '') 429 | { 430 | continue; 431 | } 432 | 433 | // Joomla change: Enforces alignment of the param variables and comments 434 | if ($previousParam !== null) 435 | { 436 | $previousName = ($previousParam['var'] !== '') ? $previousParam['var'] : 'UNKNOWN'; 437 | 438 | // Check to see if the parameters align properly. 439 | if (!$this->paramVarsAlign($param, $previousParam)) 440 | { 441 | $error = 'The variable names for parameters %s and %s do not align'; 442 | $data = array( 443 | $previousName, 444 | $param['var'] 445 | ); 446 | $phpcsFile->addError($error, $param['tag'], 'ParameterNamesNotAligned', $data); 447 | } 448 | 449 | // Check to see if the comments align properly. 450 | if (!$this->paramCommentsAlign($param, $previousParam)) 451 | { 452 | $error = 'The comments for parameters %s and %s do not align'; 453 | $data = array( 454 | $previousName, 455 | $param['var'] 456 | ); 457 | $phpcsFile->addError($error, $param['tag'], 'ParameterCommentsNotAligned', $data); 458 | } 459 | } 460 | 461 | $previousParam = $param; 462 | } 463 | 464 | $realNames = array(); 465 | 466 | foreach ($realParams as $realParam) 467 | { 468 | $realNames[] = $realParam['name']; 469 | } 470 | 471 | // Report missing comments. 472 | $diff = array_diff($realNames, $foundParams); 473 | 474 | foreach ($diff as $neededParam) 475 | { 476 | $error = 'Doc comment for parameter "%s" missing'; 477 | $data = array($neededParam); 478 | $phpcsFile->addError($error, $commentStart, 'MissingParamTag', $data); 479 | } 480 | 481 | } 482 | 483 | /** 484 | * Ensure the method's parameter comments align 485 | * 486 | * @param array $param The current parameter being checked 487 | * @param array $previousParam The previous parameter that was checked 488 | * 489 | * @return boolean 490 | */ 491 | private function paramCommentsAlign($param, $previousParam) 492 | { 493 | $paramLength = strlen($param['type']) + $param['type_space'] + strlen($param['var']) + $param['var_space']; 494 | $prevLength = strlen($previousParam['type']) + $previousParam['type_space'] + strlen($previousParam['var']) + $previousParam['var_space']; 495 | 496 | return $paramLength === $prevLength; 497 | } 498 | 499 | /** 500 | * Ensure the method's parameter variable names align 501 | * 502 | * @param array $param The current parameter being checked 503 | * @param array $previousParam The previous parameter that was checked 504 | * 505 | * @return boolean 506 | */ 507 | private function paramVarsAlign($param, $previousParam) 508 | { 509 | $paramStringLength = strlen($param['type']) + $param['type_space']; 510 | $previousParamStringLength = strlen($previousParam['type']) + $previousParam['type_space']; 511 | 512 | return $paramStringLength === $previousParamStringLength; 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /Joomla/Sniffs/Commenting/SingleCommentSniff.php: -------------------------------------------------------------------------------- 1 | getTokens(); 38 | $singleLine = false; 39 | $comment = trim($tokens[$stackPtr]['content']); 40 | 41 | // Hash comments are not allowed. 42 | if ($tokens[$stackPtr]['content'][0] === '#') 43 | { 44 | $phpcsFile->recordMetric($stackPtr, 'Inline comment style', '# ...'); 45 | 46 | $error = 'Perl-style Hash comments are prohibited. Use "// Comment."'; 47 | $error .= ' or "/* comment */" instead.'; 48 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'WrongStyle'); 49 | 50 | if ($fix === true) 51 | { 52 | $newComment = ltrim($tokens[$stackPtr]['content'], '# '); 53 | $newComment = '// ' . $newComment; 54 | $phpcsFile->fixer->replaceToken($stackPtr, $newComment); 55 | } 56 | } 57 | elseif ($tokens[$stackPtr]['content'][0] === '/' && $tokens[$stackPtr]['content'][1] === '/') 58 | { 59 | $phpcsFile->recordMetric($stackPtr, 'Inline comment style', '// ...'); 60 | $singleLine = true; 61 | } 62 | elseif ($tokens[$stackPtr]['content'][0] === '/' && $tokens[$stackPtr]['content'][1] === '*') 63 | { 64 | $phpcsFile->recordMetric($stackPtr, 'Inline comment style', '/* ... */'); 65 | } 66 | 67 | // Always have a space between // and the start of the comment text. 68 | // The exception to this is if the preceding line consists of a single open bracket. 69 | if ($tokens[$stackPtr]['content'][0] === '/' && $tokens[$stackPtr]['content'][1] === '/' && isset($tokens[$stackPtr]['content'][2]) 70 | && $tokens[$stackPtr]['content'][2] !== ' ' && isset($tokens[($stackPtr - 1)]['content'][0]) 71 | && $tokens[($stackPtr - 1)]['content'][0] !== '}' 72 | ) 73 | { 74 | $error = 'Missing space between the // and the start of the comment text.'; 75 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpace'); 76 | 77 | if ($fix === true) 78 | { 79 | $newComment = ltrim($tokens[$stackPtr]['content'], '\//'); 80 | $newComment = '// ' . $newComment; 81 | 82 | $phpcsFile->fixer->replaceToken($stackPtr, $newComment); 83 | } 84 | } 85 | 86 | /* 87 | * New lines should always start with an upper case letter, unless 88 | * the line is a continuation of a complete sentence, 89 | * the term is code and is case sensitive.(@todo) 90 | */ 91 | if (($singleLine === true && isset($tokens[$stackPtr]['content'][3]) && $tokens[$stackPtr]['content'][2] === ' ' 92 | && $tokens[$stackPtr]['content'][3] !== strtoupper($tokens[$stackPtr]['content'][3])) || (isset($comment[2]) && $comment[0] === '*' 93 | && $comment[1] === ' ' && $comment[2] !== strtoupper($comment[2])) 94 | ) 95 | { 96 | $error = 'Comment must start with a capital letter; found "%s"'; 97 | $previous = $phpcsFile->findPrevious(T_COMMENT, $stackPtr - 1); 98 | 99 | if ($singleLine === true) 100 | { 101 | $data = array($comment[3]); 102 | $newComment = ltrim($tokens[$stackPtr]['content'], '\// '); 103 | $newComment = '// ' . ucfirst($newComment); 104 | } 105 | else 106 | { 107 | $data = array($comment[2]); 108 | $padding = (strlen($tokens[$stackPtr]['content']) - strlen($comment)); 109 | $padding = str_repeat("\t", $padding - 2); 110 | $newComment = ltrim($comment, '* '); 111 | $newComment = $padding . ' * ' . ucfirst($newComment) . $phpcsFile->eolChar; 112 | } 113 | 114 | // Check for a comment on the previous line. 115 | if ($tokens[$previous]['line'] === $tokens[$stackPtr]['line'] - 1 && '/*' !== substr(ltrim($tokens[$previous]['content']), 0, 2)) 116 | { 117 | $test = trim($tokens[$previous]['content']); 118 | 119 | if ('.' === $test[(strlen($test) - 1)]) 120 | { 121 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'LowerCaseAfterSentenceEnd', $data); 122 | 123 | if ($fix === true) 124 | { 125 | $phpcsFile->fixer->replaceToken($stackPtr, $newComment); 126 | } 127 | } 128 | } 129 | else 130 | { 131 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'LowerCaseStart', $data); 132 | 133 | if ($fix === true) 134 | { 135 | $phpcsFile->fixer->replaceToken($stackPtr, $newComment); 136 | } 137 | } 138 | } 139 | 140 | /* 141 | * Comments should not be on the same line as the code to which they refer 142 | * (which puts them after the code they reference). 143 | * They should be on their own lines. 144 | * @todo Add fixer 145 | */ 146 | $previous = $phpcsFile->findPrevious(T_SEMICOLON, $stackPtr); 147 | 148 | if (isset($tokens[$previous]['line']) && $tokens[$previous]['line'] === $tokens[$stackPtr]['line']) 149 | { 150 | $error = 'Please put your comment on a separate line *preceding* your code; found "%s"'; 151 | $data = array($comment); 152 | $phpcsFile->addError($error, $stackPtr, 'SameLine', $data); 153 | } 154 | 155 | /* 156 | * Always have a single blank line before a comment or block of comments. 157 | * Don't allow preceding "code" - identified by a semicolon ;) 158 | */ 159 | if (isset($tokens[$previous]['line']) && $tokens[$previous]['line'] === $tokens[$stackPtr]['line'] - 1 160 | && $tokens[($stackPtr - 1)]['content'] !== '}' 161 | ) 162 | { 163 | $error = 'Please consider a blank line preceding your comment.'; 164 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'BlankBefore'); 165 | 166 | if ($fix === true) 167 | { 168 | $phpcsFile->fixer->addContent(($previous + 1), $phpcsFile->eolChar); 169 | } 170 | } 171 | 172 | /* 173 | * Comment blocks that introduce large sections of code and are more than 2 lines long 174 | * should use /* * / and should use * on each line with the same space/tab rules as doc 175 | * blocks. 176 | * If you need a large introduction consider whether this block should be separated into a 177 | * method to reduce complexity and therefore providing a full docblock. 178 | */ 179 | $next = $phpcsFile->findNext(T_COMMENT, $stackPtr + 1); 180 | 181 | if ($singleLine === true && isset($tokens[$next]['line']) && $tokens[$next]['line'] === $tokens[$stackPtr]['line'] + 1 182 | && $tokens[($stackPtr - 1)]['content'] !== '}' 183 | ) 184 | { 185 | // The following line contains also a comment. 186 | $nextNext = $phpcsFile->findNext(T_COMMENT, $next + 1); 187 | 188 | if ($tokens[$nextNext]['line'] === $tokens[$next]['line'] + 1) 189 | { 190 | // Found 3 lines of // comments - too much. 191 | $error = 'Use the /* */ style for comments that span over multiple lines.'; 192 | $phpcsFile->addError($error, $stackPtr, 'MultiLine'); 193 | } 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /Joomla/Sniffs/ControlStructures/ControlSignatureSniff.php: -------------------------------------------------------------------------------- 1 | 16 | * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 17 | * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 18 | * @link http://pear.php.net/package/PHP_CodeSniffer 19 | * 20 | * @since 1.0 21 | */ 22 | class Joomla_Sniffs_ControlStructures_ControlSignatureSniff implements PHP_CodeSniffer_Sniff 23 | { 24 | /** 25 | * The number of spaces code should be indented. 26 | * 27 | * @var integer 28 | */ 29 | public $indent = 1; 30 | 31 | /** 32 | * A list of tokenizers this sniff supports. 33 | * 34 | * @var array 35 | */ 36 | public $supportedTokenizers = array( 37 | 'PHP', 38 | 'JS', 39 | ); 40 | 41 | /** 42 | * Returns an array of tokens this test wants to listen for. 43 | * 44 | * @return int[] 45 | */ 46 | public function register() 47 | { 48 | return array( 49 | T_TRY, 50 | T_CATCH, 51 | T_FINALLY, 52 | T_DO, 53 | T_WHILE, 54 | T_FOR, 55 | T_FOREACH, 56 | T_IF, 57 | T_ELSE, 58 | T_ELSEIF, 59 | T_SWITCH, 60 | ); 61 | } 62 | 63 | /** 64 | * Processes this test, when one of its tokens is encountered. 65 | * 66 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 67 | * @param int $stackPtr The position of the current token in the stack passed in $tokens. 68 | * 69 | * @return void 70 | */ 71 | public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 72 | { 73 | $tokens = $phpcsFile->getTokens(); 74 | 75 | if (isset($tokens[($stackPtr + 1)]) === false) 76 | { 77 | return; 78 | } 79 | 80 | // Single space after the keyword. 81 | $found = 1; 82 | 83 | if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) 84 | { 85 | $found = 0; 86 | } 87 | elseif ($tokens[($stackPtr + 1)]['content'] !== ' ') 88 | { 89 | if (strpos($tokens[($stackPtr + 1)]['content'], $phpcsFile->eolChar) !== false) 90 | { 91 | $found = 'newline'; 92 | } 93 | else 94 | { 95 | $found = strlen($tokens[($stackPtr + 1)]['content']); 96 | } 97 | } 98 | 99 | if ($found !== 1 100 | && $tokens[($stackPtr)]['code'] !== T_ELSE 101 | && $tokens[($stackPtr)]['code'] !== T_TRY 102 | && $tokens[($stackPtr)]['code'] !== T_DO 103 | && $tokens[($stackPtr)]['code'] !== T_FINALLY 104 | ) 105 | { 106 | $error = 'Expected 1 space after %s keyword; %s found'; 107 | $data = array( 108 | strtoupper($tokens[$stackPtr]['content']), 109 | $found, 110 | ); 111 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterKeyword', $data); 112 | 113 | if ($fix === true) 114 | { 115 | if ($found === 0) 116 | { 117 | $phpcsFile->fixer->addContent($stackPtr, ' '); 118 | } 119 | else 120 | { 121 | $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' '); 122 | } 123 | } 124 | } 125 | 126 | if ($tokens[$stackPtr]['code'] === T_WHILE && !isset($tokens[$stackPtr]['scope_opener']) === true) 127 | { 128 | // Zero spaces after parenthesis closer. 129 | $closer = $tokens[$stackPtr]['parenthesis_closer']; 130 | $found = 0; 131 | 132 | if ($tokens[($closer + 1)]['code'] === T_WHITESPACE) 133 | { 134 | if (strpos($tokens[($closer + 1)]['content'], $phpcsFile->eolChar) !== false) 135 | { 136 | $found = 'newline'; 137 | } 138 | else 139 | { 140 | $found = strlen($tokens[($closer + 1)]['content']); 141 | } 142 | } 143 | 144 | if ($found !== 0) 145 | { 146 | $error = 'Expected 0 spaces before semicolon; %s found'; 147 | $data = array($found); 148 | $fix = $phpcsFile->addFixableError($error, $closer, 'SpaceBeforeSemicolon', $data); 149 | 150 | if ($fix === true) 151 | { 152 | $phpcsFile->fixer->replaceToken(($closer + 1), ''); 153 | } 154 | } 155 | }//end if 156 | 157 | // Only want to check multi-keyword structures from here on. 158 | if ($tokens[$stackPtr]['code'] === T_DO) 159 | { 160 | if (isset($tokens[$stackPtr]['scope_closer']) === false) 161 | { 162 | return; 163 | } 164 | 165 | $closer = $tokens[$stackPtr]['scope_closer']; 166 | } 167 | elseif ($tokens[$stackPtr]['code'] === T_ELSE 168 | || $tokens[$stackPtr]['code'] === T_ELSEIF 169 | || $tokens[$stackPtr]['code'] === T_CATCH 170 | ) 171 | { 172 | $closer = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true); 173 | 174 | if ($closer === false || $tokens[$closer]['code'] !== T_CLOSE_CURLY_BRACKET) 175 | { 176 | return; 177 | } 178 | } 179 | else 180 | { 181 | return; 182 | } 183 | 184 | // Own line for do, else, elseif, catch and no white space after closing brace 185 | $found = 0; 186 | 187 | if ($tokens[($closer + 1)]['code'] === T_WHITESPACE 188 | && $tokens[($closer + 1)]['content'] !== $phpcsFile->eolChar 189 | ) 190 | { 191 | $found = strlen($tokens[($closer + 1)]['content']); 192 | } 193 | 194 | if (0 !== $found) 195 | { 196 | $error = 'Expected 0 space after closing brace; %s found'; 197 | $data = array($found); 198 | $fix = $phpcsFile->addFixableError($error, $closer, 'SpaceAfterCloseBrace', $data); 199 | 200 | if (true === $fix) 201 | { 202 | $phpcsFile->fixer->replaceToken(($closer + 1), '' . $phpcsFile->eolChar); 203 | } 204 | } 205 | 206 | if ($tokens[($closer + 1)]['content'] !== $phpcsFile->eolChar && 0 === $found) 207 | { 208 | $error = 'Definition of do,else,elseif,catch must be on their own line.'; 209 | $fix = $phpcsFile->addFixableError($error, $closer, 'NewLineAfterCloseBrace'); 210 | $blanks = substr($tokens[($closer - 1)]['content'], strpos($tokens[($closer - 1)]['content'], $phpcsFile->eolChar)); 211 | $spaces = str_repeat("\t", strlen($blanks)); 212 | 213 | if (true === $fix) 214 | { 215 | $phpcsFile->fixer->beginChangeset(); 216 | $phpcsFile->fixer->addContent($closer, $phpcsFile->eolChar); 217 | $phpcsFile->fixer->addContentBefore(($closer + 1), $spaces); 218 | $phpcsFile->fixer->endChangeset(); 219 | } 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /Joomla/Sniffs/ControlStructures/ControlStructuresBracketsSniff.php: -------------------------------------------------------------------------------- 1 | getTokens(); 61 | $errorData = array(strtolower($tokens[$stackPtr]['content'])); 62 | 63 | if (isset($tokens[$stackPtr]['scope_opener']) === false) 64 | { 65 | if ($tokens[$stackPtr]['code'] !== T_WHILE) 66 | { 67 | $error = 'Possible parse error: %s missing opening or closing brace'; 68 | $phpcsFile->addWarning($error, $stackPtr, 'MissingBrace', $errorData); 69 | } 70 | 71 | return; 72 | } 73 | 74 | $openBrace = $tokens[$stackPtr]['scope_opener']; 75 | $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($openBrace - 1), $stackPtr, true); 76 | $controlStructureLine = $tokens[$lastContent]['line']; 77 | $braceLine = $tokens[$openBrace]['line']; 78 | 79 | if ($braceLine === $controlStructureLine) 80 | { 81 | $phpcsFile->recordMetric($stackPtr, 'Control Structure opening brace placement', 'same line'); 82 | $error = 'Opening brace of a %s must be on the line after the definition'; 83 | $fix = $phpcsFile->addFixableError($error, $openBrace, 'OpenBraceNewLine', $errorData); 84 | 85 | if ($fix === true) 86 | { 87 | $phpcsFile->fixer->beginChangeset(); 88 | 89 | if ($tokens[($openBrace - 1)]['code'] === T_WHITESPACE) 90 | { 91 | $phpcsFile->fixer->replaceToken(($openBrace - 1), ''); 92 | } 93 | 94 | $phpcsFile->fixer->addNewlineBefore($openBrace); 95 | $phpcsFile->fixer->endChangeset(); 96 | } 97 | 98 | return; 99 | } 100 | else 101 | { 102 | $phpcsFile->recordMetric($stackPtr, 'Control Structure opening brace placement', 'new line'); 103 | 104 | if ($braceLine > ($controlStructureLine + 1)) 105 | { 106 | $error = 'Opening brace of a %s must be on the line following the %s declaration.; Found %s line(s).'; 107 | $data = array( 108 | $tokens[$stackPtr]['content'], 109 | $tokens[$stackPtr]['content'], 110 | ($braceLine - $controlStructureLine - 1), 111 | ); 112 | $fix = $phpcsFile->addFixableError($error, $openBrace, 'OpenBraceWrongLine', $data); 113 | 114 | if ($fix === true) 115 | { 116 | $phpcsFile->fixer->beginChangeset(); 117 | 118 | for ($i = ($openBrace - 1); $i > $lastContent; $i--) 119 | { 120 | if ($tokens[$i]['line'] === ($tokens[$openBrace]['line'] + 1)) 121 | { 122 | break; 123 | } 124 | 125 | $phpcsFile->fixer->replaceToken($i, ''); 126 | } 127 | 128 | $phpcsFile->fixer->endChangeset(); 129 | } 130 | 131 | return; 132 | } 133 | } 134 | 135 | if ($tokens[($openBrace + 1)]['content'] !== $phpcsFile->eolChar) 136 | { 137 | $error = 'Opening %s brace must be on a line by itself.'; 138 | $fix = $phpcsFile->addFixableError($error, $openBrace, 'OpenBraceNotAlone', $errorData); 139 | 140 | if ($fix === true) 141 | { 142 | $phpcsFile->fixer->addNewline($openBrace); 143 | } 144 | } 145 | 146 | if ($tokens[($openBrace - 1)]['code'] === T_WHITESPACE) 147 | { 148 | $prevContent = $tokens[($openBrace - 1)]['content']; 149 | 150 | if ($prevContent === $phpcsFile->eolChar) 151 | { 152 | $spaces = 0; 153 | } 154 | else 155 | { 156 | $blankSpace = substr($prevContent, strpos($prevContent, $phpcsFile->eolChar)); 157 | $spaces = 0; 158 | 159 | /** 160 | * A tab is only counted with strlen as 1 character but we want to count 161 | * the number of spaces so add 4 characters for a tab otherwise the strlen 162 | */ 163 | for ($i = 0; $length = strlen($blankSpace), $i < $length; $i++) 164 | { 165 | if ($blankSpace[$i] === "\t") 166 | { 167 | $spaces += $this->indent; 168 | } 169 | else 170 | { 171 | $spaces += strlen($blankSpace[$i]); 172 | } 173 | } 174 | } 175 | 176 | $baseLevel = $tokens[$stackPtr]['level']; 177 | 178 | /** 179 | * Take into account any nested parenthesis that don't contribute to the level (often required for 180 | * closures and anonymous classes 181 | */ 182 | if (array_key_exists('nested_parenthesis', $tokens[$stackPtr]) === true) 183 | { 184 | $nestedStructures = $tokens[$stackPtr]['nested_parenthesis']; 185 | $nestedCount = 0; 186 | 187 | foreach ($nestedStructures as $start => $end) 188 | { 189 | /** 190 | * Crude way of checking for a chained method which requires an extra indent. We navigate to the open 191 | * parenthesis of the nested structure. The element before that is the function name. Before that we 192 | * check for an operator (->) and a whitespace before it (which makes it a chained method on a new line) 193 | * TODO: Is there a better way to check for a chained method? This feels very dirty! 194 | */ 195 | if ($tokens[$start - 2]['type'] === 'T_OBJECT_OPERATOR' && $tokens[$start - 3]['type'] === 'T_WHITESPACE') 196 | { 197 | /** 198 | * If we have an anonymous function/class on the same line as our chained method then we 199 | * balance out so only increase the count by 1. Else by 2. 200 | */ 201 | if ($tokens[$start + 1]['type'] === 'T_CLOSURE' || $tokens[$start + 1]['type'] === 'T_ANON_CLASS') 202 | { 203 | $nestedCount++; 204 | } 205 | else 206 | { 207 | $nestedCount += 2; 208 | } 209 | } 210 | else 211 | { 212 | $nestedCount++; 213 | } 214 | } 215 | 216 | $baseLevel += $nestedCount; 217 | } 218 | 219 | $expected = $baseLevel * $this->indent; 220 | 221 | // We need to divide by 4 here since there is a space vs tab intent in the check vs token 222 | $expected /= $this->indent; 223 | $spaces /= $this->indent; 224 | 225 | if ($spaces !== $expected) 226 | { 227 | $error = 'Expected %s tabs before opening brace; %s found'; 228 | $data = array( 229 | $expected, 230 | $spaces, 231 | ); 232 | $fix = $phpcsFile->addFixableError($error, $openBrace, 'SpaceBeforeBrace', $data); 233 | 234 | if ($fix === true) 235 | { 236 | $indent = str_repeat("\t", $expected); 237 | 238 | if ($spaces === 0) 239 | { 240 | $phpcsFile->fixer->addContentBefore($openBrace, $indent); 241 | } 242 | else 243 | { 244 | $phpcsFile->fixer->replaceToken(($openBrace - 1), $indent); 245 | } 246 | } 247 | } 248 | } 249 | 250 | // A single newline after opening brace (i.e. brace in on a line by itself), remove extra newlines. 251 | if (isset($tokens[$stackPtr]['scope_opener']) === true) 252 | { 253 | $opener = $tokens[$stackPtr]['scope_opener']; 254 | 255 | for ($next = ($opener + 1); $next < $phpcsFile->numTokens; $next++) 256 | { 257 | $code = $tokens[$next]['code']; 258 | 259 | if ($code === T_WHITESPACE) 260 | { 261 | continue; 262 | } 263 | 264 | // Skip all empty tokens on the same line as the opener. 265 | if ($tokens[$next]['line'] === $tokens[$opener]['line'] 266 | && (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$code]) === true 267 | || $code === T_CLOSE_TAG) 268 | ) 269 | { 270 | continue; 271 | } 272 | 273 | // We found the first bit of a code, or a comment on the following line. 274 | break; 275 | } 276 | 277 | $found = ($tokens[$next]['line'] - $tokens[$opener]['line']); 278 | 279 | if ($found > 1) 280 | { 281 | $error = 'Expected 1 newline after opening brace; %s found'; 282 | $data = array($found); 283 | $fix = $phpcsFile->addFixableError($error, $opener, 'ExtraNewlineAfterOpenBrace', $data); 284 | 285 | if ($fix === true) 286 | { 287 | $phpcsFile->fixer->beginChangeset(); 288 | 289 | for ($i = ($opener + 1); $i < $next; $i++) 290 | { 291 | if ($found > 0 && $tokens[$i]['line'] === $tokens[$next]['line']) 292 | { 293 | break; 294 | } 295 | 296 | $phpcsFile->fixer->replaceToken($i, ''); 297 | } 298 | 299 | $phpcsFile->fixer->addContent($opener, $phpcsFile->eolChar); 300 | $phpcsFile->fixer->endChangeset(); 301 | } 302 | } 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /Joomla/Sniffs/ControlStructures/WhiteSpaceBeforeSniff.php: -------------------------------------------------------------------------------- 1 | getTokens(); 90 | 91 | if (isset($tokens[$stackPtr]['scope_opener']) === false && $tokens[$stackPtr]['code'] !== T_RETURN) 92 | { 93 | return; 94 | } 95 | 96 | $prev = $phpcsFile->findPrevious(array(T_SEMICOLON, T_CLOSE_CURLY_BRACKET), ($stackPtr - 1), null, false); 97 | 98 | if ($tokens[$stackPtr]['line'] - 1 === $tokens[$prev]['line']) 99 | { 100 | $error = 'Please consider an empty line before the %s statement;'; 101 | $data = array($tokens[$stackPtr]['content']); 102 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceBefore', $data); 103 | 104 | if ($fix === true) 105 | { 106 | $phpcsFile->fixer->addNewlineBefore($stackPtr); 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Joomla/Sniffs/Functions/StatementNotFunctionSniff.php: -------------------------------------------------------------------------------- 1 | getTokens(); 47 | $nextToken = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true); 48 | 49 | if ($tokens[$nextToken]['code'] === T_OPEN_PARENTHESIS && $tokens[($stackPtr)]['code'] !== T_ECHO) 50 | { 51 | $error = '"%s" is a statement not a function; no parentheses are required'; 52 | $data = array($tokens[$stackPtr]['content']); 53 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'BracketsNotRequired', $data); 54 | 55 | if ($fix === true) 56 | { 57 | $end = $phpcsFile->findEndOfStatement($nextToken); 58 | $ignore = PHP_CodeSniffer_Tokens::$emptyTokens; 59 | $ignore[] = T_SEMICOLON; 60 | $closer = $phpcsFile->findPrevious($ignore, ($end - 1), null, true); 61 | 62 | $phpcsFile->fixer->beginChangeset(); 63 | $phpcsFile->fixer->replaceToken($nextToken, ''); 64 | 65 | if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) 66 | { 67 | $phpcsFile->fixer->replaceToken(($stackPtr + 1), ''); 68 | } 69 | 70 | if ($tokens[$closer]['code'] === T_CLOSE_PARENTHESIS) 71 | { 72 | $phpcsFile->fixer->replaceToken($closer, ''); 73 | } 74 | 75 | $phpcsFile->fixer->addContent($stackPtr, ' '); 76 | $phpcsFile->fixer->endChangeset(); 77 | } 78 | } 79 | 80 | if ($tokens[($stackPtr)]['code'] === T_ECHO 81 | && $tokens[$nextToken]['code'] === T_OPEN_PARENTHESIS 82 | && $tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE 83 | ) 84 | { 85 | $error = 'There must be one space between the "%s" statement and the opening parenthesis'; 86 | $data = array($tokens[$stackPtr]['content']); 87 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterEcho', $data); 88 | 89 | if ($fix === true) 90 | { 91 | $this->requiredSpacesBeforeOpen = 1; 92 | $padding = str_repeat(' ', $this->requiredSpacesBeforeOpen); 93 | $phpcsFile->fixer->addContent($stackPtr, $padding); 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Joomla/Sniffs/NamingConventions/ValidFunctionNameSniff.php: -------------------------------------------------------------------------------- 1 | true, 29 | 'destruct' => true, 30 | 'call' => true, 31 | 'callstatic' => true, 32 | 'get' => true, 33 | 'set' => true, 34 | 'isset' => true, 35 | 'unset' => true, 36 | 'sleep' => true, 37 | 'wakeup' => true, 38 | 'serialize' => true, 39 | 'unserialize' => true, 40 | 'tostring' => true, 41 | 'invoke' => true, 42 | 'set_state' => true, 43 | 'clone' => true, 44 | 'debuginfo' => true, 45 | ); 46 | 47 | /** 48 | * Processes the tokens within the scope. 49 | * 50 | * Extends PEAR.NamingConventions.ValidFunctionName.processTokenWithinScope to remove the requirement for leading underscores on 51 | * private method names. 52 | * 53 | * @param PHP_CodeSniffer_File $phpcsFile The file being processed. 54 | * @param integer $stackPtr The position where this token was found. 55 | * @param integer $currScope The position of the current scope. 56 | * 57 | * @return void 58 | */ 59 | protected function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope) 60 | { 61 | $methodName = $phpcsFile->getDeclarationName($stackPtr); 62 | 63 | if ($methodName === null) 64 | { 65 | // Ignore closures. 66 | return; 67 | } 68 | 69 | $className = $phpcsFile->getDeclarationName($currScope); 70 | $errorData = array($className . '::' . $methodName); 71 | 72 | // Is this a magic method. i.e., is prefixed with "__" ? 73 | if (preg_match('|^__[^_]|', $methodName) !== 0) 74 | { 75 | $magicPart = strtolower(substr($methodName, 2)); 76 | 77 | if (isset($this->magicMethods[$magicPart]) === false) 78 | { 79 | $error = 'Method name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore'; 80 | $phpcsFile->addError($error, $stackPtr, 'MethodDoubleUnderscore', $errorData); 81 | } 82 | 83 | return; 84 | } 85 | 86 | // PHP4 constructors are allowed to break our rules. 87 | if ($methodName === $className) 88 | { 89 | return; 90 | } 91 | 92 | // PHP4 destructors are allowed to break our rules. 93 | if ($methodName === '_' . $className) 94 | { 95 | return; 96 | } 97 | 98 | $methodProps = $phpcsFile->getMethodProperties($stackPtr); 99 | $scope = $methodProps['scope']; 100 | $scopeSpecified = $methodProps['scope_specified']; 101 | 102 | if ($methodProps['scope'] === 'private') 103 | { 104 | $isPublic = false; 105 | } 106 | else 107 | { 108 | $isPublic = true; 109 | } 110 | 111 | // Joomla change: Methods must not have an underscore on the front. 112 | if ($scopeSpecified === true && $methodName[0] === '_') 113 | { 114 | $error = '%s method name "%s" must not be prefixed with an underscore'; 115 | $data = array( 116 | ucfirst($scope), 117 | $errorData[0], 118 | ); 119 | 120 | $phpcsFile->addError($error, $stackPtr, 'MethodUnderscore', $data); 121 | $phpcsFile->recordMetric($stackPtr, 'Method prefixed with underscore', 'yes'); 122 | 123 | return; 124 | } 125 | 126 | /* 127 | * If the scope was specified on the method, then the method must be camel caps 128 | * and an underscore should be checked for. If it wasn't specified, treat it like a public method 129 | * and remove the underscore prefix if there is one because we cant determine if it is private or public. 130 | */ 131 | $testMethodName = $methodName; 132 | 133 | if ($scopeSpecified === false && $methodName[0] === '_') 134 | { 135 | $testMethodName = substr($methodName, 1); 136 | } 137 | 138 | if (PHP_CodeSniffer::isCamelCaps($testMethodName, false, true, false) === false) 139 | { 140 | if ($scopeSpecified === true) 141 | { 142 | $error = '%s method name "%s" is not in camel caps format'; 143 | $data = array( 144 | ucfirst($scope), 145 | $errorData[0], 146 | ); 147 | $phpcsFile->addError($error, $stackPtr, 'ScopeNotCamelCaps', $data); 148 | } 149 | else 150 | { 151 | $error = 'Method name "%s" is not in camel caps format'; 152 | $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData); 153 | } 154 | 155 | return; 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /Joomla/Sniffs/NamingConventions/ValidVariableNameSniff.php: -------------------------------------------------------------------------------- 1 | getTokens(); 36 | 37 | $varName = ltrim($tokens[$stackPtr]['content'], '$'); 38 | $memberProps = $phpcsFile->getMemberProperties($stackPtr); 39 | 40 | if (empty($memberProps) === true) 41 | { 42 | // Couldn't get any info about this variable, which generally means it is invalid or possibly has a parse 43 | // error. Any errors will be reported by the core, so we can ignore it. 44 | return; 45 | } 46 | 47 | $errorData = array($varName); 48 | 49 | if (substr($varName, 0, 1) === '_') 50 | { 51 | $error = '%s member variable "%s" must not contain a leading underscore'; 52 | $data = array( 53 | ucfirst($memberProps['scope']), 54 | $errorData[0] 55 | ); 56 | $phpcsFile->addError($error, $stackPtr, 'ClassVarHasUnderscore', $data); 57 | 58 | return; 59 | } 60 | 61 | if (PHP_CodeSniffer::isCamelCaps($varName, false, true, false) === false) 62 | { 63 | $error = 'Member variable "%s" is not in valid camel caps format'; 64 | $phpcsFile->addError($error, $stackPtr, 'MemberNotCamelCaps', $errorData); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Joomla/Sniffs/Operators/ValidLogicalOperatorsSniff.php: -------------------------------------------------------------------------------- 1 | getTokens(); 44 | $operators = array( 45 | 'and' => '&&', 46 | 'or' => '||', 47 | ); 48 | $operator = strtolower($tokens[$stackPtr]['content']); 49 | 50 | if (false === isset($operators[$operator])) 51 | { 52 | // We have correct logical operators in use so return 53 | return; 54 | } 55 | 56 | $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); 57 | 58 | if ($tokens[$nextToken]['code'] === T_EXIT) 59 | { 60 | // This enforces an exception for things like `or die;` and `or exit;` 61 | return; 62 | } 63 | 64 | // Special Joomla! cases. 65 | if ($tokens[$nextToken]['content'] === 'jexit' 66 | || $tokens[$nextToken]['content'] === 'JSession' 67 | || $tokens[$nextToken]['content'] === 'define' 68 | || $tokens[($nextToken + 2)]['content'] === 'sendResponse' 69 | || $tokens[($nextToken + 2)]['content'] === 'sendJsonResponse' 70 | ) 71 | { 72 | // Exceptions for things like `or jexit()`, `or JSession`, `or define`, `or sendResponse`, `or sendJsonResponse` 73 | return; 74 | } 75 | 76 | $error = 'Logical operator "%s" not allowed; use "%s" instead'; 77 | $data = array( 78 | $operator, 79 | $operators[$operator], 80 | ); 81 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotAllowed', $data); 82 | 83 | if (true === $fix) 84 | { 85 | $phpcsFile->fixer->replaceToken($stackPtr, $operators[$operator]); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Joomla/Sniffs/WhiteSpace/MemberVarSpacingSniff.php: -------------------------------------------------------------------------------- 1 | getTokens(); 33 | $ignore = PHP_CodeSniffer_Tokens::$methodPrefixes; 34 | $ignore[] = T_VAR; 35 | $ignore[] = T_WHITESPACE; 36 | $start = $stackPtr; 37 | $prev = $phpcsFile->findPrevious($ignore, ($stackPtr - 1), null, true); 38 | 39 | if (isset(PHP_CodeSniffer_Tokens::$commentTokens[$tokens[$prev]['code']]) === true) 40 | { 41 | // Assume the comment belongs to the member var if it is on a line by itself. 42 | $prevContent = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($prev - 1), null, true); 43 | 44 | if ($tokens[$prevContent]['line'] !== $tokens[$prev]['line']) 45 | { 46 | // Check the spacing, but then skip it. 47 | $foundLines = ($tokens[$stackPtr]['line'] - $tokens[$prev]['line'] - 1); 48 | 49 | if ($foundLines > 0) 50 | { 51 | $error = 'Expected 0 blank lines after member var comment; %s found'; 52 | $data = array($foundLines); 53 | $fix = $phpcsFile->addFixableError($error, $prev, 'AfterComment', $data); 54 | 55 | if ($fix === true) 56 | { 57 | $phpcsFile->fixer->beginChangeset(); 58 | 59 | // Inline comments have the newline included in the content but docblock do not. 60 | if ($tokens[$prev]['code'] === T_COMMENT) 61 | { 62 | $phpcsFile->fixer->replaceToken($prev, rtrim($tokens[$prev]['content'])); 63 | } 64 | 65 | for ($i = ($prev + 1); $i <= $stackPtr; $i++) 66 | { 67 | if ($tokens[$i]['line'] === $tokens[$stackPtr]['line']) 68 | { 69 | break; 70 | } 71 | 72 | $phpcsFile->fixer->replaceToken($i, ''); 73 | } 74 | 75 | $phpcsFile->fixer->addNewline($prev); 76 | $phpcsFile->fixer->endChangeset(); 77 | } 78 | } 79 | 80 | $start = $prev; 81 | } 82 | } 83 | 84 | // There needs to be 1 blank line before the var, not counting comments. 85 | if ($start === $stackPtr) 86 | { 87 | // No comment found. 88 | $first = $phpcsFile->findFirstOnLine(PHP_CodeSniffer_Tokens::$emptyTokens, $start, true); 89 | 90 | if ($first === false) 91 | { 92 | $first = $start; 93 | } 94 | } 95 | elseif ($tokens[$start]['code'] === T_DOC_COMMENT_CLOSE_TAG) 96 | { 97 | $first = $tokens[$start]['comment_opener']; 98 | } 99 | else 100 | { 101 | $first = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($start - 1), null, true); 102 | $first = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$commentTokens, ($first + 1)); 103 | } 104 | 105 | $prev = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($first - 1), null, true); 106 | $foundLines = ($tokens[$first]['line'] - $tokens[$prev]['line'] - 1); 107 | 108 | // No blank lines after class opener. 109 | if ($foundLines > 0 && $tokens[$prev]['code'] === T_OPEN_CURLY_BRACKET) 110 | { 111 | $error = 'Expected 0 blank lines before first member var; %s found'; 112 | $data = array($foundLines); 113 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'FirstMember', $data); 114 | 115 | if ($fix === true) 116 | { 117 | $phpcsFile->fixer->beginChangeset(); 118 | 119 | for ($i = ($prev + 1); $i < $first; $i++) 120 | { 121 | if ($tokens[$i]['line'] === $tokens[$prev]['line']) 122 | { 123 | continue; 124 | } 125 | 126 | if ($tokens[$i]['line'] === $tokens[$first]['line']) 127 | { 128 | break; 129 | } 130 | 131 | $phpcsFile->fixer->replaceToken($i, ''); 132 | } 133 | 134 | $phpcsFile->fixer->endChangeset(); 135 | } 136 | } 137 | 138 | if ($foundLines === 1 139 | || $tokens[$prev]['code'] === T_OPEN_CURLY_BRACKET 140 | ) 141 | { 142 | return; 143 | } 144 | 145 | $error = 'Expected 1 blank line before member var; %s found'; 146 | $data = array($foundLines); 147 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Incorrect', $data); 148 | 149 | if ($fix === true) 150 | { 151 | $phpcsFile->fixer->beginChangeset(); 152 | 153 | for ($i = ($prev + 1); $i < $first; $i++) 154 | { 155 | if ($tokens[$i]['line'] === $tokens[$prev]['line']) 156 | { 157 | continue; 158 | } 159 | 160 | if ($tokens[$i]['line'] === $tokens[$first]['line']) 161 | { 162 | $phpcsFile->fixer->addNewlineBefore($i); 163 | break; 164 | } 165 | 166 | $phpcsFile->fixer->replaceToken($i, ''); 167 | } 168 | 169 | $phpcsFile->fixer->endChangeset(); 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /Joomla/ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The Joomla coding standard. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 0 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Multi-line IF statement not indented correctly; expected %s spaces but found %s. Note: the autofixer will convert spaces to tabs 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | Multi-line function call not indented correctly; expected %s spaces but found %s. Note: the autofixer will convert spaces to tabs 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | Multi-line function declaration not indented correctly; expected %s spaces but found %s. Note: the autofixer will convert spaces to tabs 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | Object operator not indented correctly; expected %s spaces but found %s. Note: the autofixer will convert spaces to tabs 112 | 113 | 114 | 115 | 116 | 117 | error 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | Do not use global variables. Use static class properties or constants instead of globals. 146 | 147 | 148 | 149 | 150 | No scope modifier specified for function "%s 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | Closing brace indented incorrectly; expected %s spaces, found %s. Note: the autofixer will convert spaces to tabs 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Since Joomla version 4.2, Joomla uses the PSR-12 coding standard.** 2 | 3 | **We will update this soon to reflect this change** 4 | 5 | **All not php coding standards are still valid** 6 | 7 | --- 8 | 9 | Joomla Coding Standards [![Build Status](https://travis-ci.org/joomla/coding-standards.svg?branch=master)](https://travis-ci.org/joomla/coding-standards) 10 | ======================= 11 | 12 | [![Latest Stable Version](https://poser.pugx.org/joomla/coding-standards/v/stable.svg)](https://packagist.org/packages/joomla/coding-standards) [![Latest Unstable Version](https://poser.pugx.org/joomla/coding-standards/v/unstable.svg)](https://packagist.org/packages/joomla/coding-standards) [![License](https://poser.pugx.org/joomla/coding-standards/license.svg)](https://packagist.org/packages/joomla/coding-standards) 13 | 14 | This repository includes the [Joomla](https://developer.joomla.org) coding standard definition for [PHP Codesniffer](https://github.com/squizlabs/PHP_CodeSniffer) along with a few other helpful resources. The PHP_CodeSniffer standard will never be 100% accurate, but should be viewed as a strong set of guidelines while writing software for Joomla. 15 | 16 | See Joomla coding standards documentation at [https://developer.joomla.org/coding-standards.html](https://developer.joomla.org/coding-standards.html) 17 | 18 | If you want to contribute and improve this documentation, you can find the source files in the manual section of the master branch [https://github.com/joomla/coding-standards/tree/master/manual](https://github.com/joomla/coding-standards/tree/master/manual) 19 | 20 | ## Requirements 21 | 22 | * PHP 5.3+ 23 | * [PHP Codesniffer](https://github.com/squizlabs/PHP_CodeSniffer) 2.8+ 24 | 25 | **Important note**: currently the latest PHPCS is the 3.x series. There is [a development branch](https://github.com/joomla/coding-standards/tree/3.x-dev) for the Joomla custom sniffs Which is compatible with the PHPCS 3.x series. Although PEAR and composer give you the option to install PHPCS 3.x by default the Joomla custom sniffs releases are currently only compatible with PHPCS 2.x and will not work, so we remind you to always install the PHPCS 2.x series if you are using the Joomla custom sniffs. 26 | If you want to test the [Joomla Code Standard development branch for PHPCS 3.x](https://github.com/joomla/coding-standards/tree/3.x-dev), please manually install the 3.x branch. 27 | 28 | ## Installation via Composer 29 | 30 | Add `"joomla/coding-standards": "~2.0"` to the require-dev block in your composer.json and then run `composer install`. 31 | 32 | ```json 33 | { 34 | "require-dev": { 35 | "joomla/coding-standards": "~2.0" 36 | } 37 | } 38 | ``` 39 | 40 | Alternatively, you can simply run the following from the command line: 41 | 42 | ```sh 43 | composer global require squizlabs/php_codesniffer "~2.8" 44 | composer require joomla/coding-standards "~2.0" 45 | ``` 46 | 47 | As stability of joomla/coding-standards 2.0.0 is currently alpha, make sure you allow usage of alpha software in Composer: 48 | In Composer json 49 | ```json 50 | { 51 | "require-dev": { 52 | "joomla/coding-standards": "~2.0@alpha" 53 | } 54 | } 55 | ``` 56 | or on the command line 57 | ```bash 58 | composer config minimum-stability "alpha" 59 | ``` 60 | 61 | If you want to install the coding-standards globally, edit your Composer global configuration by running: 62 | 63 | ```bash 64 | composer global config -e 65 | ``` 66 | 67 | and add the following: `"minimum-stability": "alpha"` 68 | 69 | Once you have the coding standard files you can tell PHPCS where the standard folder is (i.e. install them in PHPCS) 70 | ```sh 71 | phpcs --config-set installed_paths /path/to/joomla-coding-standards 72 | ``` 73 | Note: the composer scripts will run when the standard is installed globally, but not when it's a dependency. As such, you may want to run PHPCS config-set. When you run PHPCS config-set it will always overwrite the previous values. Use `--config-show` to check previous values before using `--config-set` 74 | So instead of overwriting the existing paths you should copy the existing paths revealed with `--config-show` and add each one separated by a comma: 75 | `phpcs --config-set installed_paths [path_1],[path_2],[/path/to/joomla-coding-standards]` 76 | 77 | ## Running 78 | 79 | You can use the installed Joomla standard like: 80 | 81 | phpcs --standard=Joomla path/to/code 82 | 83 | Alternatively if it isn't installed you can still reference it by path like: 84 | 85 | phpcs --standard=path/to/joomla/coding-standards path/to/code 86 | 87 | ### Selectively Applying Rules 88 | 89 | For consuming packages there are some items that will typically result in creating their own project ruleset.xml, rather than just directly using the Joomla ruleset.xml. A project ruleset.xml allows the coding standard to be selectively applied for excluding 3rd party libraries, for consideration of backwards compatibility in existing projects, or for adjustments necessary for projects that do not need PHP 5.3 compatibility (which will be removed in a future version of the Joomla Coding Standard in connection of the end of PHP 5.3 support in all active Joomla Projects). 90 | 91 | For information on [selectively applying rules read details in the PHP CodeSniffer wiki](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml#selectively-applying-rules) 92 | 93 | #### Common Rule Set Adjustments 94 | 95 | The most common adjustment is to exclude folders with 3rd party libraries, or where the code has yet to have coding standards applied. 96 | 97 | ```xml 98 | 99 | build/* 100 | tests/* 101 | 102 | 103 | libraries/* 104 | vendor/* 105 | ``` 106 | 107 | Another common adjustment is to exclude the [camelCase format requirement](https://developer.joomla.org/coding-standards/php-code.html) for "Classes, Functions, Methods, Regular Variables and Class Properties" the essentially allows for B/C with snake_case variables which were only allowed in the context of interacting with the database. 108 | 109 | ```xml 110 | 111 | 112 | 113 | 114 | 115 | 116 | ``` 117 | 118 | Old Protected method names were at one time prefixed with an underscore. These Protected method names with underscores are deprecated in Joomla projects but for B/C reasons are still in the projects. As such, excluding the MethodUnderscore sniff is a common ruleset adjustment 119 | 120 | ```xml 121 | 122 | 123 | 124 | 125 | ``` 126 | 127 | The last most common adjustment is removing PHP 5.3 specific rules which prevent short array syntax, and allowing short array syntax for method parameters. 128 | 129 | ```xml 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | ``` 140 | ## Using example rulesets that Selectively Applying Rule 141 | You have to tell you can tell PHPCS where the example ruleset folder is (i.e. install them in PHPCS) 142 | ```sh 143 | phpcs --config-set installed_paths /path/to/joomla/coding-standards/Example-Rulesets 144 | ``` 145 | Note: the composer scripts will run when the standard is installed globally, but not when it's a dependency. As such, you may want to run PHPCS config-set. When you run PHPCS config-set it will always overwrite the previous values. Use `--config-show` to check previous values before using `--config-set` 146 | So instead of overwriting the existing paths you should copy the existing paths revealed with `--config-show` and add each one separated by a comma: 147 | `phpcs --config-set installed_paths [path_1],[path_2],[/path/to/joomla-coding-standards],[/path/to/joomla/coding-standards/Example-Rulesets]` 148 | 149 | ## IDE autoformatters 150 | 151 | There is further information on how to set up IDE auto formatters here: 152 | 153 | [https://github.com/joomla/coding-standards/tree/master/Joomla/IDE](https://github.com/joomla/coding-standards/tree/master/Joomla/IDE) 154 | 155 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joomla/coding-standards", 3 | "type": "phpcodesniffer-standard", 4 | "description": "Joomla Coding Standards", 5 | "keywords": ["joomla", "coding standards", "phpcs", "php codesniffer"], 6 | "homepage": "https://github.com/joomla/coding-standards", 7 | "license": "GPL-2.0-or-later", 8 | "authors": [ 9 | { 10 | "name": "Joomla Coding Standards Contributors", 11 | "homepage": "https://github.com/joomla/coding-standards/graphs/contributors" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=5.3.10", 16 | "squizlabs/php_codesniffer": "^2.8.0" 17 | }, 18 | "require-dev": { 19 | "phpunit/phpunit": "^4.8.7" 20 | }, 21 | "extra": { 22 | "branch-alias": { 23 | "dev-master": "2.x-dev" 24 | } 25 | }, 26 | "scripts": { 27 | "post-install-cmd": "\"vendor/bin/phpcs\" --config-set installed_paths ../../..", 28 | "post-update-cmd" : "\"vendor/bin/phpcs\" --config-set installed_paths ../../.." 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /manual/README.md: -------------------------------------------------------------------------------- 1 | # Joomla Coding Standards Manual 2 | This manual documents the Joomla Coding Standards 3 | 4 | ## Meta Information 5 | The documentation in this repository is optimised for display in the [coding standards section](https://developer.joomla.org/coding-standards.html) of the Joomla! Developer Network. When formatting and proposing changes to this documentation, please keep the following in mind: 6 | 7 | - Heading levels should start at `

` 8 | - The document title does NOT need to be included in the document, these are displayed as the page heading based on the Joomla component configuration and would result in duplicated titles if included 9 | - Because the GitHub Markdown parser interprets line breaks as `
` tags, arbitrary line breaks to keep lines from becoming too long should not be used 10 | - Links to documents in this manual should be formatted for use on the Joomla! Developer Network, such as `[a link to the Basic Guidelines page](/coding-standards/basic-guidelines.html)` 11 | -------------------------------------------------------------------------------- /manual/appendices/analysis.md: -------------------------------------------------------------------------------- 1 | For new contributions we are going to be enforcing coding standards to ensure that the coding style in the source code is consistent. Ensuring that your code meets these standards will make the process of code contribution smoother. 2 | 3 | ### Configuring Code Analysis Tools 4 | 5 | In order to improve the consistency and readability of the source code, the Joomla project runs a coding style analysis tool, PHP_CodeSniffer, everytime changes are pushed to a Joomla project's repository. 6 | 7 | #### Running PHP_CodeSniffer 8 | 9 | The Joomla Coding Standards sniffer rules are written to be used with a tool called PHP CodeSniffer. Please see the [PHP_CodeSniffer PEAR Page](http://pear.php.net/package/PHP_CodeSniffer) for information on installing PHP_CodeSniffer on your system. 10 | 11 | You can run the CodeSniffer by going to the CMS, Framework, or Issue Tracker's root directory and executing 12 | 13 | ``` 14 | phpcs --report=checkstyle --report-file=build/logs/checkstyle.xml --standard=/path/to//build/phpcs/Joomla /path/to/ 15 | ``` 16 | 17 | Alternatively, if you have Ant installed on your system, you may run the CodeSniffer by going to the `` of the Joomla project's code you want to test and executing 18 | 19 | ``` 20 | ant phpcs 21 | ``` 22 | 23 | ### Other Tools 24 | 25 | Here are some other tools available to developers who are planning to submit source code to the project. 26 | 27 | #### Set up PHP_CodeSniffer 28 | 29 | See in the documentation https://docs.joomla.org/Joomla_CodeSniffer#3._IDE_Integration 30 | -------------------------------------------------------------------------------- /manual/appendices/examples.md: -------------------------------------------------------------------------------- 1 | Please contribute to this documentation page. 2 | -------------------------------------------------------------------------------- /manual/basic-guidelines.md: -------------------------------------------------------------------------------- 1 | This chapter outlines the basic guidelines covering files contributed to the Joomla project. The most important rule to remember when coding for the Joomla project, **if in doubt, please ask**. 2 | 3 | ### File Format 4 | 5 | All files contributed to Joomla must be: 6 | 7 | * Stored as ASCII text 8 | Exceptions: languages locales and some test files containing non-ASCII characters 9 | * Use UTF-8 character encoding 10 | * Be Unix formatted following these rules. 11 | 1. Lines must end only with a line feed (LF). 12 | 2. Line feeds are represented as ordinal 10, octal 012 and hex 0A. 13 | 3. Do not use carriage returns (CR) like Macintosh computers do or the carriage return/line feed combination (CRLF) like Windows computers do. 14 | 15 | ### Spelling 16 | 17 | The spelling of words and terms used in code comments and in the naming of class, functions, variables and constant should generally be in accordance with British English rules (en\_GB). Some exceptions are permitted, for example where common programming names are used that align with the PHP API or other established conventions such as for `color` where is it common practice to maintain US English spelling. 18 | 19 | ### Indenting 20 | 21 | Tabs are used for indenting code (not spaces as required by the PEAR standard). Source code editors or Integrated Development Environments (IDE’s) such as Eclipse must have the tab-stops for indenting measuring four (4) spaces in length. 22 | 23 | ### Line Length 24 | 25 | There is no maximum limit for line lengths in files, however, a notional value of about 150 characters is recommended to achieve a good level of readability without horizontal scrolling. Longer lines are permitted if the nature of the code for individual lines requires it and line breaks would have an adverse affect on the final output (such as for mixed PHP/HTML layout files). 26 | -------------------------------------------------------------------------------- /manual/css.md: -------------------------------------------------------------------------------- 1 | These guidelines have been assembled following an examination of emerging practices, ideas and existing styleguides, namely: 2 | 3 | 1. [OOCSS Code Standards](https://github.com/stubbornella/oocss-code-standards) 4 | 2. [Oneweb Style Guide](https://github.com/nternetinspired/OneWeb/blob/master/STYLEGUIDE.md) 5 | 3. [Idiomatic CSS](https://github.com/necolas/idiomatic-css) 6 | 7 | ### Commenting 8 | 9 | #### Major sections 10 | Major code sections should be named in caps and within a full comment block, eg: 11 | 12 | ```css 13 | /* ========================================================================== 14 | PRIMARY NAVIGATION 15 | ========================================================================== */ 16 | ``` 17 | 18 | #### Sub sections 19 | Subsections should be normally cased and within an open comment block. 20 | 21 | ```css 22 | /* Mobile navigation 23 | ========================================================================== */ 24 | ``` 25 | 26 | #### Verbose comments 27 | 28 | ```css 29 | /** 30 | * Short description using Doxygen-style comment format 31 | * 32 | * The first sentence of the long description starts here and continues on this 33 | * line for a while finally concluding here at the end of this paragraph. 34 | * 35 | * The long description is ideal for more detailed explanations and 36 | * documentation. It can include example HTML, URLs, or any other information 37 | * that is deemed necessary or useful. 38 | * 39 | * @tag This is a tag named 'tag' 40 | * 41 | * TODO: This is a todo statement that describes an atomic task to be completed 42 | * at a later date. It wraps after 80 characters and following lines are 43 | * indented by 2 spaces. 44 | */ 45 | ``` 46 | 47 | #### Basic comments 48 | 49 | ```css 50 | /* Basic comment */ 51 | ``` 52 | 53 | #### Uncompiled LESS/Scss comments 54 | 55 | ```css 56 | // These are stripped on compile. 57 | ``` 58 | 59 | ### CSS selectors 60 | Only use classes for CSS selectors, *never IDs* as they introduce unwanted specificity to the cascade. Using an ID as a CSS selector is like firing the first nuke; you begin a specifity war that can only escalate, with terrible consequence. 61 | 62 | To put it another way; Don't use a Sith Lord when just two Storm Troopers will suffice: [CSS Specificity Wars](http://www.stuffandnonsense.co.uk/archives/css_specificity_wars.html) 63 | 64 | #### Class naming convention 65 | Use dashes to create compound class names: 66 | 67 | ```css 68 | /* Good - use dashes */ 69 | .compound-class-name {…} 70 | 71 | /* Good - uses camelCase */ 72 | .compoundClassName {…} 73 | 74 | /* Bad - uses underscores */ 75 | .compound_class_name {…} 76 | 77 | /* Bad - does not use seperators */ 78 | .compoundclassname {…} 79 | ``` 80 | 81 | #### Indentation 82 | Rules should be indented one tab (equal to 4 spaces): 83 | 84 | ```css 85 | /* Good */ 86 | .example { 87 | color: #000; 88 | visibility: hidden; 89 | } 90 | 91 | /* Bad - all on one line */ 92 | .example {color: #000; visibility: hidden;} 93 | ``` 94 | 95 | #### Alignment 96 | The opening brace must be on the same line as the last selector and preceded by a space. The closing brace must be on its own line after the last property and be indented to the same level as the opening brace. 97 | 98 | ```css 99 | /* Good */ 100 | .example { 101 | color: #fff; 102 | } 103 | 104 | /* Bad - closing brace is in the wrong place */ 105 | .example { 106 | color: #fff; 107 | } 108 | 109 | /* Bad - opening brace missing space */ 110 | .example{ 111 | color: #fff; 112 | } 113 | ``` 114 | 115 | #### Property Format 116 | Each property must be on its own line and indented one level. There should be no space before the colon and one space after. All properties must end with a semicolon. 117 | 118 | ```css 119 | /* Good */ 120 | .example { 121 | background: black; 122 | color: #fff; 123 | } 124 | 125 | /* Bad - missing spaces after colons */ 126 | .example { 127 | background:black; 128 | color:#fff; 129 | } 130 | 131 | /* Bad - missing last semicolon */ 132 | .example { 133 | background: black; 134 | color: #fff 135 | } 136 | ``` 137 | 138 | #### HEX values 139 | HEX values must be declared in lowercase and shorthand: 140 | 141 | ```css 142 | /* Good */ 143 | .example { 144 | color: #eee; 145 | } 146 | 147 | /* Bad */ 148 | .example { 149 | color: #EEEEEE; 150 | } 151 | ``` 152 | 153 | #### Attribute selectors 154 | Always use double quotes around attribute selectors. 155 | 156 | ```css 157 | /* Good */ 158 | input[type="button"] { 159 | ... 160 | } 161 | 162 | /* Bad - missing quotes */ 163 | input[type=button] { 164 | ... 165 | } 166 | 167 | /* Bad - using single quote */ 168 | input[type='button'] { 169 | ... 170 | } 171 | ``` 172 | 173 | #### Zero value units 174 | Zero values should not carry units. 175 | 176 | ```css 177 | /* Good */ 178 | .example { 179 | padding: 0; 180 | } 181 | 182 | /* Bad - uses units */ 183 | .example { 184 | padding: 0px; 185 | } 186 | ``` 187 | -------------------------------------------------------------------------------- /manual/docblocks.md: -------------------------------------------------------------------------------- 1 | Documentation headers for PHP code in: files, classes, class properties, methods and functions, called the docblocks, follow a convention similar to JavaDoc or phpDOC. 2 | 3 | These "DocBlocks" borrow from the PEAR standard but have some variations specific for Joomla and the Joomla projects. 4 | 5 | Whereas normal code indenting uses real tabs, all whitespace in a Docblock uses real spaces. This provides better readability in source code browsers. The minimum whitespace between any text elements, such as tags, variable types, variable names and tag descriptions, is two real spaces. Variable types and tag descriptions should be aligned according to the longest Docblock tag and type-plus-variable respectively. 6 | 7 | Code contributed to the Joomla project that will become the copyright of the project is not allowed to include @author tags. Joomla's philosophy is that the code is written "all together" and there is no notion of any one person "owning" any section of code. The @author tags are permitted in third-party libraries that are included in the core libraries. 8 | 9 | Files included from third party sources must leave DocBlocks intact. Layout files use the same DocBlocks as other PHP files. 10 | 11 | ### File DocBlock Headers 12 | The file header DocBlock consists of the following required and optional elements in the following order: 13 | Short description (optional unless the file contains more than two classes or functions), followed by a blank line). Long description (optional, followed by a blank line). 14 | 15 | * @version (optional and must be first) 16 | * @category (optional and rarely used) 17 | * @package (generally optional but required when files contain only procedural code. Always optional in namespaced code) 18 | * @subpackage (optional) 19 | * @author (optional but only permitted in non-Joomla source files) 20 | * @copyright (required) 21 | * @license (required and must be compatible with the Joomla license) 22 | * @link (optional) 23 | * @see (optional) 24 | * @since (generally optional but required when files contain only procedural code) 25 | * @deprecated (optional) 26 | 27 | Example of a DocBlock Header: 28 | 29 | ```php 30 | /** 31 | * @package Joomla.Installation 32 | * @subpackage Controller 33 | * 34 | * @copyright Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved. 35 | * @license GNU General Public License version 2 or later; see LICENSE.txt 36 | */ 37 | ``` 38 | 39 | ### Class Definitions 40 | Class definitions start on a new line and the opening and closing braces are also placed on new lines. Class methods must follow the guidelines for Function Definitions. Properties and methods must follow OOP standards and be declared appropriately (using public, protected, private and static as applicable). Class definitions, properties and methods must each be provided with a DocBlock in accordance with the following sections. 41 | 42 | #### Class DocBlock Headers 43 | The class Docblock consists of the following required and optional elements in the following order. 44 | Short description (required, unless the file contains more than two classes or functions), followed by a blank line). Long description (optional, followed by a blank line). 45 | 46 | * @category (optional and rarely used) 47 | * @package (optional) 48 | * @subpackage (optional) 49 | * @author (optional but only permitted in non-Joomla source files) 50 | * @copyright (optional unless different from the file Docblock) 51 | * @license (optional unless different from the file Docblock) 52 | * @link (optional) 53 | * @see (optional) 54 | * @since (required, being the version of the software the class was introduced) 55 | * @deprecated (optional) 56 | 57 | Example of a Class file DocBlock header: 58 | ```php 59 | /** 60 | * Controller class to initialise the database for the Joomla Installer. 61 | * 62 | * @package Joomla.Installation 63 | * @subpackage Controller 64 | * @since 3.1 65 | */ 66 | ``` 67 | 68 | #### Class Property DocBlocks 69 | The class property Docblock consists of the following required and optional elements in the following order. 70 | Short description (required, followed by a blank line) 71 | 72 | * @var (required, followed by the property type) 73 | * @since (required) 74 | * @deprecated (optional) 75 | 76 | Example of Class property DocBlock: 77 | 78 | ```php 79 | /** 80 | * The generated user ID 81 | * 82 | * @var integer 83 | * @since 3.1 84 | */ 85 | protected static $userId = 0; 86 | ``` 87 | 88 | #### Class Method DocBlocks and Functions DocBlocks 89 | Function definitions start on a new line and the opening and closing braces are also placed on new lines. An empty line should precede lines specifying the return value. 90 | 91 | Function definitions must include a documentation comment in accordance with the Commenting section of this document. 92 | 93 | * Short description (required, followed by a blank line) 94 | * Long description (optional, followed by a blank line) 95 | * @param (required if there are method or function arguments, the last @param tag is followed by a blank line) 96 | * @return (required, followed by a blank line) 97 | * @since (required, followed by a blank line if there are additional tags) 98 | * @throws (required if method or function arguments throws a specific type of exception) 99 | * All other tags in alphabetical order. 100 | 101 | **Note:** 102 | Commonly a line after the tag @param consists of the following three parts in order of appearance: 103 | * variable type (There must be 3 spaces before variable type.) 104 | * variable name (There must be 2 spaces after the longest type.) 105 | * variable description (There must be 2 spaces after the longest variable name.) 106 | 107 | If there are more than one @param the type, names and description have to be aligned. 108 | 109 | Example of Method DocBlock: 110 | ```php 111 | /** 112 | * Set a controller class suffix for a given HTTP method. 113 | * 114 | * @param string $method The HTTP method for which to set the class suffix. 115 | * @param string $suffix The class suffix to use when fetching the controller name for a given request. 116 | * 117 | * @return JApplicationWebRouter This object for method chaining. 118 | * 119 | * @since 12.2 120 | * 121 | * @throws InvalidArgumentException Thrown if the provided arguments is not of type string. 122 | * @throws \UnexpectedValueException May be thrown if the container has not been set. 123 | */ 124 | public function setHttpMethodSuffix($method, $suffix) 125 | ``` 126 | 127 | If a function definition goes over multiple lines, all lines must be indented with one tab and the closing bracket must go on the same line as the last parameter. 128 | 129 | ```php 130 | function fooBar($param1, $param2, 131 | $param3, $param4) 132 | { 133 | // Body of method. 134 | } 135 | ``` 136 | -------------------------------------------------------------------------------- /manual/html.md: -------------------------------------------------------------------------------- 1 | These guidelines have been assembled following an examination of emerging practices, ideas and existing styleguides, and include items from: 2 | 3 | 1. [Google's HTML styleguide](https://google.github.io/styleguide/htmlcssguide.html) 4 | 2. [jQuery's HTML Styleguide](http://contribute.jquery.org/style-guide/html/) 5 | 3. [Nicolas Ghallager's "Principles of writing consistent, idiomatic HTML"](https://github.com/necolas/idiomatic-html) 6 | 4. [Harry Robert's "My HTML/CSS coding style"](http://csswizardry.com/2012/04/my-html-css-coding-style/) 7 | 4. [The BBC's Media Standards and Guidelines](http://www.bbc.co.uk/guidelines/futuremedia/technical/semantic_markup.shtml) 8 | 9 | ### Doctype 10 | 11 | Always use the minimal, versionless doctype. 12 | 13 | ```html 14 | 15 | ``` 16 | 17 | ### Language 18 | 19 | Always define which language the page is written in. 20 | 21 | ```html 22 | 23 | ``` 24 | 25 | ### Encoding 26 | 27 | Always define the character encoding. The encoding should be defined as early as possible. Make sure your editor uses UTF-8 as character encoding, without a byte order mark (UTF-8, no BOM). Do not specify the encoding of style sheets as these assume UTF-8. 28 | 29 | ```html 30 | 31 | ``` 32 | 33 | More on encodings and when and how to specify them can be found in [Handling character encodings in HTML and CSS](http://www.w3.org/International/tutorials/tutorial-char-enc/) 34 | 35 | ### Capitalisation 36 | All HTML should be lowercase; element names, attributes, attribute values (unless text/CDATA), CSS selectors, properties, and property values (with the exception of strings). Additionally, there is no need to use CDATA to escape inline JavaScript, formerly a requirement to meet XML strictness in XHTML. 37 | 38 | ```html 39 | 40 | Joomla 41 | 42 | 43 | Home 44 | ``` 45 | 46 | ```html 47 | 48 | a { 49 | color: #a3a3a3; 50 | } 51 | 52 | 53 | a { 54 | color: #A3A3A3; 55 | } 56 | ``` 57 | 58 | ### Protocol 59 | 60 | Omit the protocol portion (http:, https:) from URLs pointing to images and other media files, style sheets, and scripts unless they are not available over both protocols. 61 | 62 | This prevents mixed content issues and results in minor file size savings. 63 | 64 | ```html 65 | 66 | 67 | 68 | 69 | 70 | ``` 71 | 72 | ### Elements and Attributes 73 | 74 | Always include ``, ``, and `` tags. 75 | 76 | ### Type attributes 77 | 78 | Do not use type or attributes for style sheets (unless not using CSS) and scripts (unless not using JavaScript). 79 | 80 | ```html 81 | 82 | 83 | 84 | 85 | 86 | ``` 87 | 88 | ### Language attributes 89 | 90 | Do not use language attributes on script tags. 91 | ```html 92 | 93 | 106 | 107 | 108 | 109 | ``` 110 | 111 | Use attribute/value pairs for boolean attributes 112 | 113 | ```html 114 | 115 | 116 | 117 | 118 | 119 | ``` 120 | 121 | HTML attributes should be listed in an order that reflects the fact that class names are the primary interface through which CSS and JavaScript select elements. 122 | 123 | 1. class 124 | 2. id 125 | 3. data-* 126 | 4. Everything else 127 | 128 | ```html 129 | 130 | Text 131 | 132 | 133 | Text 134 | ``` 135 | 136 | Elements with multiple attributes can have attributes arranged across multiple lines in an effort to improve readability and produce more useful diffs: 137 | 138 | ```html 139 | 143 | Text 144 | 145 | ``` 146 | 147 | ### Elements 148 | 149 | Optional closing tags may not be omitted. 150 | 151 | ```html 152 | 153 |

The quick brown fox jumps over the lazy dog.

154 | 155 | 156 |

The quick brown fox jumps over the lazy dog. 157 | ``` 158 | 159 | Self-closing (void) elements should not be closed. Trailing forward slashes and spaces should be omitted. 160 | 161 | ```html 162 | 163 | 164 | 165 | 166 | 167 | ``` 168 | 169 | ### Formatting 170 | 171 | Use a new line for every block, list, or table element, and indent every such child element. 172 | 173 | ```html 174 | 175 |

176 |
    177 |
  • Home
  • 178 |
  • Blog
  • 179 |
180 |
181 | 182 | 183 |
    184 |
  • Home
  • 185 |
  • Blog
  • 186 |
187 | ``` 188 | 189 | We prefer readability over file-size savings when it comes to maintaining existing files. Plenty of whitespace is encouraged. Use whitespace to visually separate groups of related markup and to improve the readability and maintainability of your HTML. Use two empty lines between larger blocks, and use a single empty line between child blocks of larger blocks. Be consistent. (If you are worried about your document's size, spaces (as well as repeated use of the same strings - for instance class names) are excellent candidates for compression. Also, you may use a markup minifier to decrease your document's file size.) 190 | 191 | Keep line-length to a sensible maximum, e.g., 80 columns. 192 | 193 | Tip: configure your editor to "show invisibles". This will allow you to eliminate end of line whitespace, eliminate unintended blank line whitespace, and avoid polluting commits. 194 | 195 | ```html 196 |
197 |

Space, the final frontier.

198 |
199 | 200 | 201 |
    202 |
  • Moe
  • 203 |
  • Larry
  • 204 |
  • Curly
  • 205 |
206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 |
IncomeTaxes
$ 5.00$ 4.50
221 | ``` 222 | 223 | ### Indentation 224 | 225 | Don't indent inside ``, ``, ` 243 | 248 | 249 | 250 | 251 |

Joomla! is awesome!

252 | 253 | 254 | 255 | ``` 256 | 257 | ### Trailing Whitespace 258 | 259 | Remove trailing white spaces. Trailing white spaces are unnecessary and can complicate diffs. 260 | 261 | ```html 262 | 263 |

Yes please.

264 | 265 | 266 |

No, thank you.

267 | ``` 268 | 269 | ### Entity References 270 | 271 | Do not use entity references. There is no need to use entity references like —, ”, or ☺, assuming the same encoding (UTF-8) is used for files and editors as well as among teams. 272 | 273 | The only exceptions apply to characters with special meaning in HTML (like < and &) as well as control or “invisible” characters (like no-break spaces). 274 | 275 | ```html 276 | 277 |

The currency symbol for the Euro is “€”.

278 | 279 | 280 |

The currency symbol for the Euro is “&eur;”.

281 | ``` 282 | 283 | ### Inline CSS 284 | 285 | Inline CSS must be avoided. When altering states using JavaScript, use CSS to define your states, and only use unobtrusive JavaScript to alter class names whenever possible. 286 | 287 | ```html 288 | 289 | Home 290 | 291 | 292 | Home 293 | ``` 294 | 295 | @todo more meaningful example. 296 | 297 | ### Style Attributes 298 | 299 | You should not use border, align, valign, or clear attributes. Avoid use of style attributes, except where using syndicated content or internal syndicating systems. 300 | 301 | ### Semantics 302 | 303 | Use HTML according to its purpose. For example, use heading elements for headings, `

` elements for paragraphs, `` elements for anchors, etc. 304 | 305 | Using HTML according to its purpose is important for accessibility, reuse, and code efficiency reasons. 306 | 307 | ```html 308 | 309 | View subscriptions 310 | 311 | 312 |

View subscriptions
313 | ``` 314 | 315 | ### Markup 316 | 317 | #### Image Tags 318 | 319 | Image elements (``) must have an alt attribute. Height and width attributes are optional and may be omitted. 320 | 321 | @todo add examples from here http://www.bbc.co.uk/guidelines/futuremedia/technical/semantic_markup.shtml 322 | 323 | ### Comments 324 | 325 | @todo: comment styles in JS, CSS, HTML 326 | 327 | For more complex blocks of HTML, it may be useful to add a comment to the closing tag: 328 | 329 | ```html 330 |
331 | 332 |
333 |
334 | 335 |
336 | ``` 337 | 338 | ### Mark todos 339 | Highlight todos by using the keyword TODO, eg: 340 | 341 | ```html 342 | 343 |
    344 |
  • Home
  • 345 |
  • Blog
  • 346 |
347 | ``` 348 | 349 | ### Adding line breaks 350 | 351 | Always use `
` instead of `
` and `
` eg: 352 | 353 | ```html 354 | This text contains
a line break. 355 | ``` 356 | 357 | ### Markup validation tools 358 | 359 | @todo: list various testing tools: 360 | 361 | * http://validator.w3.org/nu/ 362 | * http://csslint.net/ 363 | -------------------------------------------------------------------------------- /manual/ini.md: -------------------------------------------------------------------------------- 1 | .ini Language Files Format 2 | 3 | An .ini file is composed of strings and comments. See [Specification of language files](https://docs.joomla.org/Specification_of_language_files). 4 | 5 | ### Formatting 6 | 7 | #### Comments 8 | There are 2 types of comments: 9 | - Comments used as sections, to clarify the use of a group of strings. 10 | - Comments used to inform translators of some characteristics of the string(s) just below. These should not be preceded by a blank line. 11 | 12 | #### Strings 13 | - All language strings are in British English (en-GB). See [Joomla! en-GB User Interface Text Guidelines](https://developer.joomla.org/en-gb-user-interface-text-guidelines/introduction.html) 14 | - The lines commented should start with a semi-colon. 15 | - The strings should be alphabetically ordered using the "Standard Alphabetical Order" and not the "ASCII order". This mostly means here that the underscore is sorted BEFORE the letters. 16 | 17 | Example: 18 | ```ini 19 | COM_ABCD_FORMAT_WHATEVER 20 | ``` 21 | comes before 22 | ```ini 23 | COM_ABCD_FORMATTING_WHATEVER 24 | ``` 25 | 26 | Some specific text editors only sort by "ASCII order" and should not be used. 27 | 28 | ### Plugins 29 | Some IDEs have specific plugins to sort the lines by "Standard Alphabetical Order". 30 | 31 | Examples: 32 | - Atom [Sort selected elements](https://github.com/BlueSilverCat/sort-selected-elements) 33 | - Eclipse [Sortit](https://github.com/channingwalton/eclipse-sortit/tree/master/com.teaminabox.eclipse.sortit) 34 | - NetBeans [Sort line tools](http://plugins.netbeans.org/plugin/45925/sort-line-tools) 35 | - PhpStorm [String Manipulation](https://plugins.jetbrains.com/plugin/2162-string-manipulation) 36 | 37 | It may be necessary to set the sorting to "Case Insensitive Sort" to obtain the correct results. 38 | 39 | ### Online tools 40 | There is also an online tool which lets order this way (among other goodies): https://wordcounter.net/alphabetize 41 | 42 | ### Notes 43 | When the file includes commented sections, the strings should be alphabetically ordered in each section. 44 | Warning: when the comments concern specific strings and sorting has modified the order, the comments should be moved to the right place. -------------------------------------------------------------------------------- /manual/inline-comments.md: -------------------------------------------------------------------------------- 1 | This chapter covers inline code commenting in more detail. Inline comments are all comments not included in doc blocs. The goal of in line commenting is to explain code in context. Such explanation may take many different forms. 2 | 3 | Comments that are written in a readable and narrative style, especially when explaining a complex process, are encouraged. In general they should be placed close to the code explained rather than before the entire block of code. 4 | 5 | Comments should be as sentence like as possible, which is to say that they should be complete and readable, not in short hand. 6 | 7 | Comments are not a replacement for detailed doc blocks. Comments serve to explain the logic and structure of code itself rather than the use of code. 8 | 9 | ### Formatting of Comments 10 | 11 | Comment blocks that introduce large sections of code and are more than 2 lines long should use `/* */` (C) style and should use `*` on each line with the same space/tab rules as doc blocks. If you need a large introduction consider whether this block should be separated into a method to reduce complexity and therefore providing a full docblock. 12 | 13 | Comments should precede the code they refer to. As a corollary, comments should not be on the same line as the code to which they refer (which puts them after the code they reference). They should be on their own lines. 14 | 15 | Don’t use a blank line between comments and the code they refer to (no space underneath a comment block). 16 | 17 | Always have a single blank line before a comment or block of comments unless the comment (or block) is at the beginning of a code structure. ( You should not have a blank line after a '{' line ) 18 | 19 | For example in the following case there is no new line before the first comment (because it follows a '{' line) but we do want a new line before the second comment: 20 | 21 | ```php 22 | while (!$done) 23 | { 24 | // We don't want an infinite loop here. 25 | $done = true; 26 | } 27 | 28 | // Now let's do something interesting. 29 | $result = somethingInteresting(); 30 | ``` 31 | 32 | Comments should align with the code they refer to, using the same indenting as the line that follows the comment. 33 | 34 | Comments should be indented with tabs (like code, not like doc blocks). 35 | 36 | ### Content of comments 37 | 38 | Comments should use en-GB 39 | 40 | Always have a space between // and the start of comment text. 41 | 42 | New lines should always start with an upper case letter unless: 43 | * The line is a continuation of a complete sentence. 44 | * The term is code and is case sensitive. 45 | 46 | Code that is included specifically to assure compatibility with other software (for example specific browsers or a specific version of the CMS or for backward compatibility reasons) should be clearly marked as such. If there is the intention to remove specific code at a future point, state that but do not use a deprecation tag or specify a release (this can be hard to predict). 47 | 48 | Check spelling and grammar on all comments (see below and [the en-GB guidelines](https://developer.joomla.org/en-gb-user-interface-text-guidelines.html)). 49 | 50 | Only end a line with a period if it is a full sentence. 51 | 52 | Rather than use docblock tags use See:, Link: and Note: for comments if appropriate. 53 | 54 | Do not use HTML in comments unless specifically related to the comment content. 55 | 56 | Do not leave commented code unless there is a clearly explained reason for doing so. In such a case, a comment saying "Code intentionally commented" will prevent accidental removal. 57 | 58 | Comments may include forward looking statements of how something will be extended or modified in the future, but not todos indicating that code is not complete or ready to use. 59 | 60 | Remember that humor and sarcasm are difficult to translate. 61 | 62 | ### Acronyms 63 | 64 | Capitalise all letters of acronyms such as HTML, XML, SQL, GMT, and UTC. 65 | 66 | ### Common spelling and grammar errors for which to check. 67 | 68 | Joomla contributors include many non-native speakers of en-GB which makes it understandable that there are sometimes spelling and grammar errors. At the same time, some people reading comments also are non native speakers and may even use automated translation to understand comments. This makes it very important that comments should follow proper en-GB spelling and grammar rules. Unfortunately, these can be tricky. 69 | 70 | #### S vs Z 71 | 72 | en-GB most commonly uses `ise` where en-US would use `ize`, such as `normalise` instead of `normalize`. (Note that there are some exceptions to this rule.) 73 | 74 | Use of apostrophes is one of the trickier parts of English. 75 | 76 | #### Lets versus let’s 77 | 78 | Lets means permits or allows to: 79 | 80 | ```php 81 | // This lets the user enter data 82 | ``` 83 | 84 | Let’s is a contraction for let us and means we are going to do this now: 85 | 86 | ```php 87 | // Let's validate the field 88 | ``` 89 | 90 | #### Its versus it’s 91 | 92 | Its is the possessive form of it: 93 | 94 | ```php 95 | // Get its ID 96 | ``` 97 | 98 | It’s is a contraction of it is 99 | 100 | ```php 101 | // It's time to save 102 | ``` 103 | 104 | #### The correct Joomla spelling of some commonly used words. 105 | 106 | - dependant 107 | - deprecated 108 | -------------------------------------------------------------------------------- /manual/introduction.md: -------------------------------------------------------------------------------- 1 | Good coding standards are important in any software development project. These standards are even more important when a large, diverse and worldwide community of developers are contributing to a project. One of the things that sets good software apart from great software is not only the features or the actual function the software performs, but the quality of its source code. 2 | 3 | In order to perform in the highly competitive Open Source and proprietary software industries, source code not only needs to be beautifully designed, it also needs to be beautiful and elegant to look at. The Joomla Coding Standards Manual is to help ensure our code is the highest quality which will make it easy to read, debug, and maintain. 4 | 5 | ### Guiding Principles 6 | 7 | Since readable code is more maintainable, the compass that guides us in achieving that goal is a set of well thought out coding standards for the different software languages that are employed in the Joomla software project. Joomla has a solid heritage of striving to support a great looking product with great looking code. 8 | 9 | This manual compiles the collective wisdom of past and present contributors to the project to form the definitive standard for coding in Joomla, whether that is for the core Joomla Framework or an extension that forms part of the stack of the Joomla CMS. This manual also serves as a lighthouse to the Joomla developer community, to safely guide developers around the pitfalls of becoming lackadaisical with respect to writing clean, beautiful code. 10 | 11 | The Joomla Coding Standards borrows heavily from the PEAR coding standard for PHP files, augmenting and diverging where it is deemed sensible to do so. 12 | 13 | There are [tools available](/coding-standards/analysis.html) to help your code conform to our standards. 14 | 15 | ### Contributing to this manual 16 | 17 | If you want to contribute and improve this manual, you can find the source files at [https://github.com/joomla/coding-standards/tree/master/manual](https://github.com/joomla/coding-standards/tree/master/manual). 18 | -------------------------------------------------------------------------------- /manual/javascript-j3.md: -------------------------------------------------------------------------------- 1 | ### Contents 2 | 3 | 1. [Naming Conventions](#naming-conventions) 4 | - [Variables](#naming-conventions-variables) 5 | - [Functions](#naming-conventions-functions) 6 | - [Reserved Words](#naming-conventions-reserved) 7 | 2. [Syntax Style](#syntax-style) 8 | - [Indentation](#syntax-indentation) 9 | - [Spacing](#syntax-spacing) 10 | - [Commas](#syntax-commas) 11 | - [Semicolons](#syntax-semicolons) 12 | - [Quotes](#syntax-quotes) 13 | 3. [Types](#types) 14 | 4. [Functions](#functions) 15 | 5. [Conditional Statements](#conditional-statements) 16 | 6. [Blocks & Multi-line Statements](#blocks) 17 | 7. [Comments](#comments) 18 | 19 | 20 | ### Naming Conventions 21 | 22 | Use descriptive words or terse phrases for names. 23 | 24 | Variables and Functions should be camel case, starting with a lowercase letter: `likeThis` 25 | 26 | 27 | #### Variables 28 | 29 | **Use names that describe what the variable is:** 30 | 31 | `var element = document.getElementById('elementId');` 32 | 33 | **Iterators are the exception** 34 | 35 | Use i for index in a loop (and subsequent letters when necessary for nested iteration). 36 | 37 | 38 | #### Functions 39 | 40 | **Use names that describe what the function does:** 41 | 42 | ```js 43 | function getSomeData() { 44 | // statements 45 | } 46 | ``` 47 | 48 | 49 | #### Reserved Words 50 | 51 | Do not use reserved words for anything other than their intended use. The list of: [Reserved Words](http://es5.github.io/#x7.6.1) 52 | 53 | --- 54 | 55 | 56 | ### Syntax Style 57 | 58 | 59 | #### Indentation 60 | - Don't mix tabs and spaces. 61 | - Tabs, 4 spaces 62 | 63 | 64 | #### Spacing 65 | - No whitespace at the end of line or on blank lines. 66 | - Unary special-character operators (e.g., !, ++) must not have space next to their operand. 67 | - Any , and ; must not have preceding space. 68 | - Any ; used as a statement terminator must be at the end of the line. 69 | - Any : after a property name in an object definition must not have preceding space. 70 | - The ? and : in a ternary conditional must have space on both sides. 71 | - No filler spaces in empty constructs (e.g., {}, [], fn()) 72 | - New line at the end of each file. 73 | 74 | **Array:** 75 | 76 | ```js 77 | var array = [ 'foo', 'bar' ]; 78 | ``` 79 | 80 | **Function call:** 81 | 82 | ```js 83 | foo( arg ); 84 | ``` 85 | 86 | **Function call with multiple arguments:** 87 | 88 | ```js 89 | foo( 'string', object ); 90 | ``` 91 | 92 | **Conditional Statements** 93 | 94 | ```js 95 | if ( condition ) { 96 | // statements 97 | } else { 98 | // statements 99 | } 100 | ``` 101 | 102 | ```js 103 | while ( condition ) { 104 | // statements 105 | } 106 | ``` 107 | 108 | ```js 109 | for ( prop in object ) { 110 | // statements 111 | } 112 | ``` 113 | 114 | 115 | ##### Exceptions 116 | 117 | **First or only argument is an object, array or callback function.** 118 | 119 | **No space before the first argument:** 120 | 121 | ```js 122 | foo({ 123 | a: 'bar', 124 | b: 'baz' 125 | }); 126 | ``` 127 | 128 | ```js 129 | foo(function() { 130 | // statements 131 | }, options ); // space after options argument 132 | ``` 133 | 134 | **Function with a callback, object, or array as the last argument:** 135 | 136 | No space after the last argument. 137 | 138 | ```js 139 | foo( data, function() { 140 | // statements 141 | }); 142 | ``` 143 | 144 | 145 | #### Commas 146 | 147 | **Place commas after:** 148 | 149 | - variable declarations 150 | - key/value pairs 151 | 152 | #### Arrays 153 | 154 | Do not include a trailing comma, this will add 1 to your array's length. 155 | 156 | **No:** 157 | 158 | ```js 159 | array = [ 'foo', 'bar', ]; 160 | ``` 161 | 162 | **Yes:** 163 | 164 | ```js 165 | array = [ 'foo', 'bar' ]; 166 | ``` 167 | 168 | 169 | #### Semicolons 170 | 171 | Use them where expected. 172 | 173 | Semicolons should be included at the end of function expressions, but not at the end of function declarations. 174 | 175 | **Function Expression:** 176 | 177 | ```js 178 | var foo = function() { 179 | return true; 180 | }; 181 | ``` 182 | 183 | **Function Declaration:** 184 | 185 | ```js 186 | function foo() { 187 | return true; 188 | } 189 | ``` 190 | 191 | 192 | #### Quotes 193 | 194 | Use ' instead of " 195 | 196 | 197 | --- 198 | 199 | 200 | ### Variables 201 | 202 | ### Avoid Global Variables 203 | 204 | **No:** 205 | 206 | ```js 207 | foo = 'bar'; 208 | ``` 209 | 210 | **No: implied global** 211 | 212 | ```js 213 | function() { 214 | foo = 'bar'; 215 | } 216 | ``` 217 | 218 | **Yes:** 219 | 220 | ```js 221 | var foo = 'bar'; 222 | ``` 223 | 224 | #### Multiple Variable Declarations 225 | 226 | Use one `var` declaration and separate each variable on a newline ending with a comma. 227 | 228 | **No:** 229 | 230 | ```js 231 | var foo = 'bar'; 232 | var bar = 'baz'; 233 | var baz = 'qux'; 234 | ``` 235 | 236 | **Yes:** 237 | 238 | ```js 239 | var foo = 'bar', 240 | bar = 'baz', 241 | baz = 'qux'; 242 | ``` 243 | 244 | 245 | --- 246 | 247 | 248 | ### Types 249 | 250 | #### String 251 | 252 | - Concatenate long strings. 253 | - Place space before closing quote at the end of each string. 254 | - Concatenation operator at the end of each subsequent string. 255 | 256 | ```js 257 | var longString = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ' + 258 | 'Sed placerat, tellus eget egestas tincidunt, lectus dui ' + 259 | 'sagittis massa, id mollis est tortor a enim. In hac ' + 260 | 'habitasse platea dictumst. Duis erat justo, tincidunt ac ' + 261 | 'enim iaculis, malesuada condimentum mauris. Vestibulum vel ' + 262 | 'cursus mauris.'; 263 | ``` 264 | 265 | #### Number 266 | 267 | Use `parseInt()` or `parseFloat()` instead of unary plus, for readability. 268 | 269 | **No:** 270 | 271 | ```js 272 | var count = +document.getElementById('inputId').value; 273 | ``` 274 | 275 | **Yes:** 276 | 277 | ```js 278 | var count = parseInt(document.getElementById('inputId').value); 279 | ``` 280 | 281 | #### Type Checks 282 | 283 | - String: `typeof object === 'string'` 284 | - Number: `typeof object === 'number'` 285 | - Boolean: `typeof object === 'boolean'` 286 | - Object: `typeof object === 'object'` 287 | - Plain Object: `jQuery.isPlainObject( object )` 288 | - Function: `jQuery.isFunction( object )` 289 | - Array: `jQuery.isArray( object )` 290 | - Element: `object.nodeType` 291 | - null: `object === null` 292 | - null or undefined: `object == null` 293 | 294 | **Undefined:** 295 | 296 | - Global Variables: `typeof variable === 'undefined'` 297 | - Local Variables: `variable === undefined` 298 | - Properties: `object.prop === undefined` 299 | 300 | #### Objects 301 | 302 | Use the literal, not constructor, syntax. 303 | 304 | **No:** 305 | 306 | ```js 307 | var myObj = new Object(); 308 | ``` 309 | 310 | **Yes:** 311 | 312 | ```js 313 | var myObj = {}; 314 | ``` 315 | 316 | If an object contains more than one key/value pair or an array as a value, each key/value pair should be on its own line. 317 | 318 | ```js 319 | var myObj = { 320 | foo: 'bar', 321 | bar: 'baz', 322 | baz: 'qux' 323 | }; 324 | ``` 325 | 326 | #### Arrays 327 | 328 | Use the literal, not constructor, syntax 329 | 330 | **No:** 331 | 332 | ```js 333 | var myArr = new Array(); 334 | ``` 335 | 336 | **Yes:** 337 | 338 | ```js 339 | var myArr = []; 340 | ``` 341 | 342 | If you don't know array length use push method. This will replace the current array. 343 | 344 | ```js 345 | var myArr = []; 346 | myArr.push('foo'); 347 | ``` 348 | 349 | 350 | --- 351 | 352 | 353 | ### Functions 354 | 355 | #### Chaining Method Calls 356 | 357 | ```js 358 | $('.someElement') 359 | .hide() 360 | .delay(1000) 361 | .fadeIn(); 362 | ``` 363 | 364 | 365 | --- 366 | 367 | 368 | ### Conditional Statements 369 | 370 | Use ternary syntax if: 371 | 372 | - One condition 373 | - Result of either evaluation is one operation. 374 | 375 | ```js 376 | joomlaRocks ? 'This is true' : 'else it is false'; 377 | ``` 378 | 379 | Otherwise, use standard syntax: 380 | 381 | ```js 382 | if ( condition ) { 383 | // statements 384 | } else { 385 | // statements 386 | } 387 | ``` 388 | 389 | **Cache length in variable for performance:** 390 | 391 | ```js 392 | var i, 393 | j = myArr.length; 394 | 395 | for ( i = 0; i < j; i++ ) { 396 | // statements 397 | } 398 | ``` 399 | 400 | **With more than one condition:** 401 | 402 | ```js 403 | if ( condition && 404 | condition2 && 405 | condition3 ) { 406 | // statements 407 | } else { 408 | // statements 409 | } 410 | ``` 411 | 412 | #### Equality 413 | 414 | Use strict equality operator === so that type is considered in comparison. Using == can produce false positives. 415 | 416 | 417 | ```js 418 | // evaluates true 419 | 1 == "1" 420 | ``` 421 | 422 | ```js 423 | // evaluates false 424 | 1 === "1" 425 | ``` 426 | 427 | 428 | --- 429 | 430 | 431 | ### Blocks & Multi-line Statements 432 | 433 | Use curly braces on blocks that have more than one statement. 434 | 435 | **Block with one statement:** 436 | 437 | ```js 438 | if ( test ) return false; 439 | ``` 440 | 441 | **Block with more than one statement:** 442 | 443 | ```js 444 | if ( test ) { 445 | var foo = 'some string'; 446 | return foo; 447 | } 448 | ``` 449 | 450 | 451 | --- 452 | 453 | 454 | ### Comments 455 | 456 | **Single Line** 457 | 458 | - Place above the code it refers to. 459 | - A space between double forward slashes and comment text. 460 | 461 | ```js 462 | // I am a single line comment. 463 | ``` 464 | 465 | 466 | **Multiline** 467 | 468 | - Place above the code it refers to. 469 | - Opening token placed on the line above first comment line, closing placed below last comment line. 470 | - Each comment line begins with two astericks followed by a space. 471 | 472 | ```js 473 | /* 474 | ** I am a multiline comment. 475 | ** Line two 476 | ** Line three 477 | */ 478 | ``` 479 | 480 | --- 481 | 482 | ### TODO 483 | 484 | - Switch Statements vs other methods like Objects 485 | - Add jQuery examples 486 | - Double check accuracy of all examples 487 | 488 | **With help from:** 489 | 490 | - [jQuery JS Style Guide](https://contribute.jquery.org/style-guide/js) 491 | - [Idiomatic JS](https://github.com/rwaldron/idiomatic.js) 492 | -------------------------------------------------------------------------------- /manual/javascript-j4.md: -------------------------------------------------------------------------------- 1 | ### Javascript in Joomla 4 2 | 3 | Joomla 4 uses ES6 syntax where possible. As part of this change we decided to use an industry standard codestyle rules for our JavaScript - the AirBNB coding standards. These can be found [on their GitHub page](https://github.com/airbnb/javascript#table-of-contents). 4 | 5 | We have four modifications to the defaults: 6 | 7 | 1. We allow parameter reassignment of function parameters to allow easier setting of defaults (for examples please see the ESLint rules [here](https://eslint.org/docs/rules/no-param-reassign)) 8 | 9 | 2. We only allow the imports of dependencies from NPM in our build directory (`build/`) (rather than in all files) 10 | 11 | 3. We currently allow browser alerts using the `alert()` function (the long term intention is to disable this as we move to our custom element alerts) 12 | 13 | 4. We always enable JavaScript strict mode as we aren't using JavaScript modules at this time 14 | 15 | We also use ESLint to enforce these rules. If you are familiar with JavaScript from other projects you've worked on you can find our ESLint rules [on GitHub](https://github.com/joomla/joomla-cms/blob/4.0-dev/.eslintrc) 16 | -------------------------------------------------------------------------------- /manual/license.md: -------------------------------------------------------------------------------- 1 | The contents of the Joomla! en-GB User Interface Text Guidelines are subject to copyright law and are made available under the [Joomla! Electronic Documentation License (JEDL)](https://docs.joomla.org/JEDL) unless otherwise stated. 2 | 3 | You may find the [JEDL Frequently Asked Questions](https://docs.joomla.org/JEDL/FAQ) useful in determining if your proposed use of this material is allowed. 4 | 5 | If you have any questions regarding licensing of this material please contact legal@opensourcematters.org. 6 | 7 | If you wish to report a possible violation of the license terms for the material on this site then please contact legal@opensourcematters.org. 8 | -------------------------------------------------------------------------------- /manual/php.md: -------------------------------------------------------------------------------- 1 | > **Note** 2 | > 3 | > The Joomla! CMS switched in Version 4.2.0 from its own coding standard to the PSR-12 (and later to PER Coding Style) coding standard. 4 | > This document applies to Joomla! < 4.2.0 and parts of it still applies to 4.2.0 and later. 5 | > 6 | > This document will be updated to reflect the PSR-12 coding standard soon. 7 | 8 | ### Language Constructs 9 | 10 | #### PHP Code Tags 11 | 12 | Always use the full `` to delimit PHP code, not the `` shorthand. This is the most portable way to include PHP code on differing operating systems and setups. 13 | 14 | For files that contain only PHP code, the closing tag (`?>`) should not be included. It is not required by PHP. Leaving this out prevents trailing white space from being accidentally injected into the output that can introduce errors in the Joomla session (see the PHP manual on [Instruction separation](https://secure.php.net/basic-syntax.instruction-separation)). 15 | 16 | Files should always end with a blank new line. 17 | 18 | #### General 19 | 20 | Pursuant to PSR-2 [Keywords and True/False/Null][] 21 | 22 | > PHP [keywords][] MUST be in lower case. 23 | > The PHP constants `true`, `false`, and `null` MUST be in lower case. 24 | 25 | [Keywords and True/False/Null]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md#25-keywords-and-truefalsenull 26 | [keywords]: https://secure.php.net/manual/en/reserved.keywords.php 27 | 28 | #### Including Code 29 | 30 | Anywhere you are unconditionally including a file, use `require_once`. Anywhere you are conditionally including a file (for example, factory methods), use `include_once`. Either of these will ensure that files are included only once. They share the same file list, so you don't need to worry about mixing them. A file included with `require_once` will not be included again by `include_once`. 31 | 32 | > **Note** 33 | > 34 | > `include_once` and `require_once` are PHP language statements, not functions. The correct formatting is: 35 | > 36 | > 37 | > `require_once JPATH_COMPONENT . '/helpers/helper.php';` 38 | 39 | You should not enclose the filename in parentheses. 40 | 41 | #### E_STRICT Compatible PHP Code 42 | 43 | As of Joomla version 1.6 and for all versions of the Joomla Platform, adhering to object oriented programming practice as supported by PHP 5.3+ is required. Joomla is committed to progressively making the source code E_STRICT. 44 | 45 | ### Global Variables 46 | 47 | Global variables should not be used. Use static class properties or constants instead of globals, following OOP and factory patterns. 48 | 49 | ### Error Surpression 50 | 51 | The use of the `@` for Error Surpression should be avoided and limited to use when no other approach or workaround is available. 52 | 53 | ### Control Structures (General Code) 54 | 55 | For all control structures there is a space between the keyword and an opening parenthesis, then no space either after the opening parenthesis or before the closing bracket. This is done to distinguish control keywords from function names. All control structures must contain their logic within braces. 56 | 57 | For all all control structures, such as `if`, `else`, `do`, `for`, `foreach`, `try`, `catch`, `switch` and `while`, both the keyword starts a newline and the opening and closing braces are each put on a new line. 58 | 59 | Exclamation mark `!`, the logical operator `not` used in a condition, should not have spaces before or after the exclamation mark as shown in the examples. 60 | 61 | #### An _if-else_ Example 62 | 63 | ```php 64 | if ($test) 65 | { 66 | echo 'True'; 67 | } 68 | 69 | // Comments can go here. 70 | // Note that "elseif" as one word is used. 71 | elseif ($test === false) 72 | { 73 | echo 'Really false'; 74 | } 75 | elseif (!$condition) 76 | { 77 | echo 'Not Condition'; 78 | } 79 | else 80 | { 81 | echo 'A white lie'; 82 | } 83 | ``` 84 | 85 | If a control structure goes over multiple lines, all lines must be indented with one tab and the closing brace must go on the same line as the last parameter. 86 | 87 | ```php 88 | if ($test1 89 | && $test2) 90 | { 91 | echo 'True'; 92 | } 93 | ``` 94 | 95 | #### A _do-while_ Example 96 | 97 | ```php 98 | do 99 | { 100 | $i++; 101 | } 102 | while ($i < 10); 103 | ``` 104 | 105 | #### A _for_ Example 106 | 107 | ```php 108 | for ($i = 0; $i < $n; $i++) 109 | { 110 | echo 'Increment = ' . $i; 111 | } 112 | ``` 113 | 114 | #### A _foreach_ Example 115 | 116 | ```php 117 | foreach ($rows as $index => $row) 118 | { 119 | echo 'Index = ' . $index . ', Value = ' . $row; 120 | } 121 | ``` 122 | 123 | #### A _while_ Example 124 | 125 | ```php 126 | while (!$done) 127 | { 128 | $done = true; 129 | } 130 | ``` 131 | 132 | #### A _switch_ example 133 | 134 | When using a `switch` statement, the `case` keywords are indented. The `break` statement starts on a newline assuming the indent of the code within the case. 135 | 136 | ```php 137 | switch ($value) 138 | { 139 | case 'a': 140 | echo 'A'; 141 | break; 142 | 143 | default: 144 | echo 'I give up'; 145 | break; 146 | } 147 | ``` 148 | 149 | #### A _try catch_ example 150 | ```php 151 | try 152 | { 153 | $table->bind($data); 154 | } 155 | catch (RuntimeException $e) 156 | { 157 | throw new Exception($e->getMessage(), 500, $e); 158 | } 159 | ``` 160 | 161 | ### Mixed language usage (e.g. at the layout files) 162 | 163 | For layout files and all files where we use a mix of PHP and HTML (all PHP files in the `view/tmpl` and `layout` folder) we additionally wrap every line into a `` block and use the alternative syntax for control structures. This should make the code easier to read and make it easier to move blocks around without creating fatal errors due to missing `` tags. 164 | 165 | #### Example Control Structures 166 | 167 | ##### An _if-else_ Example 168 | 169 | ```php 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | ``` 178 | 179 | ### References 180 | 181 | When using references, there should be a space before the reference operator and no space between it and the function or variable name. 182 | 183 | For example: 184 | 185 | ```php 186 | $ref1 = &$this->sql; 187 | ``` 188 | 189 | > **Note** 190 | > 191 | > In PHP 5, reference operators are not required for objects. All objects are handled by reference. 192 | 193 | 194 | ### Concatenation Spacing 195 | There should always be a space before and after the concatenation operator ('.'). For example: 196 | 197 | ```php 198 | $id = 1; 199 | echo JRoute::_('index.php?option=com_foo&task=foo.edit&id=' . (int) $id); 200 | ``` 201 | 202 | If the concatenation operator is the first or last character on a line, both spaces are not required. For example: 203 | 204 | ```php 205 | $id = 1 206 | echo JRoute::_( 207 | 'index.php?option=com_foo&task=foo.edit&id=' . (int) $id 208 | . '&layout=special' 209 | ); 210 | ``` 211 | 212 | ### Arrays 213 | 214 | Assignments (the `=>` operator) in arrays may be aligned with spaces. When splitting array definitions onto several lines, the last value should also have a trailing comma. This is valid PHP syntax and helps to keep code diffs minimal. Joomla 3 prefers `array()` to be backward compatible to 5.3.10 and Joomla 4.0.0 onwards should use the short array syntax `[]` by default. (Short array syntax was introduced in PHP 5.4.) 215 | 216 | For example: 217 | 218 | ```php 219 | $options = [ 220 | 'foo' => 'foo', 221 | 'spam' => 'spam', 222 | ]; 223 | 224 | ``` 225 | 226 | ### Code Commenting 227 | 228 | Inline comments to explain code follow the convention for C (`/* … */`) and C++ single line (`// ...`) comments. C-style blocks are generally restricted to documentation headers for files, classes and functions. The C++ style is generally used for making code notes. Code notes are strongly encouraged to help other people, including your future-self, follow the purpose of the code. Always provide notes where the code is performing particularly complex operations. 229 | 230 | Perl/shell style comments (`#`) are not permitted in PHP files. 231 | 232 | Blocks of code may, of course, be commented out for debugging purposes using any appropriate format, but should be removed before submitting patches for contribution back to the core code. 233 | 234 | For example, do not include feature submissions like: 235 | 236 | ```php 237 | // Must fix this code up one day. 238 | //$code = broken($fixme); 239 | ``` 240 | 241 | More details on inline code comments can be found in the chapter on [Inline Code Comments](/coding-standards/inline-code-comments.html). 242 | 243 | #### Comment Docblocks 244 | 245 | Documentation headers for PHP and Javascript code in files, classes, class properties, methods and functions, called the docblocks, follow a convention similar to JavaDoc or phpDOC. 246 | 247 | These "DocBlocks" borrow from the PEAR standard but have some variations specific for Joomla and the Joomla Platform. 248 | 249 | More details on DocBlocks comments can be found in the chapter on [DocBlocks Comments](/coding-standards/docblocks.html). 250 | 251 | ### Function Calls 252 | 253 | Functions should be called with no spaces between the function name and the opening parenthesis, and no space between this and the first parameter; a space after the comma between each parameter (if they are present), and no space between the last parameter and the closing parenthesis. There should be space before and exactly one space after the equals sign. Tab alignment over multiple lines is permitted. 254 | 255 | ```php 256 | // An isolated function call. 257 | $foo = bar($var1, $var2); 258 | 259 | // Multiple aligned function calls. 260 | $short = bar('short'); 261 | $medium = bar('medium'); 262 | $long = bar('long'); 263 | ``` 264 | 265 | ### Function Definitions 266 | 267 | Function definitions start on a new line with no spaces between the function name and the opening parenthesis. Additionally, the opening and closing braces are also placed on new lines. An empty line should precede lines specifying the return value. 268 | 269 | Function definitions must include a documentation comment in accordance with the Commenting section of this document. More details on DocBlocks Function comments can be found in the chapter on [DocBlocks Comments](/coding-standards/docblocks.html). 270 | 271 | ```php 272 | /** 273 | * A utility function. 274 | * 275 | * @param string $path The library path in dot notation. 276 | * 277 | * @return void 278 | * 279 | * @since 1.6 280 | */ 281 | function jimport($path) 282 | { 283 | // Body of method. 284 | } 285 | ``` 286 | 287 | If a function definition goes over multiple lines, all lines must be indented with one tab and the closing brace must go on the same line as the last parameter. 288 | 289 | ```php 290 | function fooBar($param1, $param2, 291 | $param3, $param4) 292 | { 293 | // Body of method. 294 | } 295 | ``` 296 | 297 | ### Closures / Anonymous functions 298 | Closures/Anonymous functions should have a space between the Closure's/Anonymous function's name and the opening parenthesis. Method signatures don't have the space. 299 | 300 | ```php 301 | $fieldIds = array_map( 302 | function ($f) 303 | { 304 | return $f->id; 305 | }, 306 | $fields 307 | ); 308 | ``` 309 | 310 | ### Class Definitions 311 | 312 | Class definitions start on a new line and the opening and closing braces are also placed on new lines. Class methods must follow the guidelines for Function Definitions. Properties and methods must follow OOP standards and be declared appropriately (using public, protected, private and static as applicable). 313 | 314 | Class definitions, properties and methods must each be provided with a DocBlock in accordance with the following sections. 315 | 316 | More details on DocBlocks Class comments can be found in the chapter on [DocBlocks Comments](/coding-standards/docblocks.html). 317 | 318 | #### Class Property DocBlocks 319 | 320 | More details on Class Property DocBlocks can be found in the chapter on [DocBlocks Comments](docblocks.md). 321 | 322 | #### Class Method DocBlocks 323 | 324 | The DocBlock for class methods follows the same convention as for PHP functions. 325 | 326 | More details on DocBlocks Class Method comments can be found in the chapter on [DocBlocks Comments](docblocks.md). 327 | 328 | ### Class Definition Example 329 | ```php 330 | /** 331 | * A utility class. 332 | * 333 | * @package Joomla.Platform 334 | * @subpackage XBase 335 | * @since 1.6 336 | */ 337 | class JClass extends JObject 338 | { 339 | /** 340 | * Human readable name 341 | * 342 | * @var string 343 | * @since 1.6 344 | */ 345 | public $name; 346 | 347 | /** 348 | * Method to get the name of the class. 349 | * 350 | * @param string $case Optionally return in upper/lower case. 351 | * 352 | * @return boolean True if successfully loaded, false otherwise. 353 | * 354 | * @since 1.6 355 | */ 356 | public function getName($case = null) 357 | { 358 | // Body of method. 359 | return $this->name; 360 | } 361 | } 362 | ``` 363 | 364 | ### Naming Conventions 365 | 366 | #### Classes 367 | 368 | Classes should be given descriptive names. Avoid using abbreviations where possible. Class names should always begin with an uppercase letter and be written in CamelCase even if using traditionally uppercase acronyms (such as XML, HTML). One exception is for Joomla Platform classes which must begin with an uppercase 'J' with the next letter also being uppercase. 369 | 370 | For example: 371 | 372 | - JHtmlHelper 373 | - JXmlParser 374 | - JModel 375 | 376 | #### Functions and Methods 377 | 378 | Functions and methods should be named using the "studly caps" style (also referred to as "bumpy case" or "camel caps"). The initial letter of the name is lowercase, and each letter that starts a new "word" is capitalized. Function in the Joomla framework must begin with a lowercase 'j'. 379 | 380 | For example: 381 | 382 | - connect(); 383 | - getData(); 384 | - buildSomeWidget(); 385 | - jImport(); 386 | - jDoSomething(); 387 | 388 | Private class members (meaning class members that are intended to be used only from within the same class in which they are declared) are preceded by a single underscore. Properties are to be written in underscore format (that is, logical words separated by underscores) and should be all lowercase. 389 | 390 | For example: 391 | 392 | ```php 393 | class JFooHelper 394 | { 395 | protected $field_name = null; 396 | 397 | private $_status = null; 398 | 399 | protected function sort() 400 | { 401 | // Code goes here 402 | } 403 | } 404 | ``` 405 | 406 | #### Namespaces 407 | 408 | Namespaces are formatted according to this flow. First there is the file docblock followed by the namespace the file lives in. When required, the namespace is followed by the `defined` check. Lastly, the imported classes using the `use` keyword. All namespace imports must be alphabetically ordered. 409 | 410 | ```php 411 | /** 412 | * @package Joomla.Administrator 413 | * @subpackage mod_quickicon 414 | * 415 | * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. 416 | * @license GNU General Public License version 2 or later; see LICENSE.txt 417 | */ 418 | 419 | namespace Joomla\Module\Quickicon\Administrator\Helper; 420 | 421 | defined('_JEXEC') or die; 422 | 423 | use Joomla\CMS\Factory; 424 | use Joomla\CMS\Language\Text; 425 | use Joomla\CMS\Plugin\PluginHelper; 426 | use Joomla\CMS\Router\Route; 427 | use Joomla\Module\Quickicon\Administrator\Event\QuickIconsEvent; 428 | ``` 429 | 430 | #### Constants 431 | 432 | Constants should always be all-uppercase, with underscores to separate words. Prefix constant names with the uppercase name of the class/package they are used in. For example, the constants used by the `JError` class all begin with `JERROR_`. 433 | 434 | #### Regular Variables and Class Properties 435 | 436 | Regular variables, follow the same conventions as function. 437 | 438 | Class variables should be set to null or some other appropriate default value. 439 | 440 | ### Exception Handling 441 | 442 | Exceptions should be used for error handling. 443 | 444 | The following sections outline how to semantically use [SPL exceptions](https://secure.php.net/manual/en/spl.exceptions.php). 445 | 446 | #### Logic Exceptions 447 | 448 | The LogicException is thrown when there is an explicit problem with the way the API is being used. For example, if a dependency has failed (you try to operate on an object that has not been loaded yet). 449 | 450 | The following child classes can also be used in appropriate situations: 451 | 452 | ##### BadFunctionCallException 453 | 454 | This exception can be thrown if a callback refers to an undefined function or if some arguments are missing. For example if `is_callable()`, or similar, fails on a function. 455 | 456 | ##### BadMethodCallException 457 | 458 | This exception can be thrown if a callback refers to an undefined method or if some arguments are missing. For example `is_callable()`, or similar, fails on a class method. Another example might be if arguments passed to a magic call method are missing. 459 | 460 | ##### InvalidArgumentException 461 | 462 | This exception can be thrown if there is invalid input. 463 | 464 | ##### DomainException 465 | 466 | This exception is similar to the InvalidArgumentException but can be thrown if a value does not adhere to a defined valid data domain. For example trying to load a database driver of type "mongodb" but that driver is not available in the API. 467 | 468 | ##### LengthException 469 | 470 | This exception can be thrown is a length check on an argument fails. For example a file signature was not a specific number of characters. 471 | 472 | ##### OutOfRangeException 473 | 474 | This exception has few practical applications but can be thrown when an illegal index was requested. 475 | 476 | #### Runtime Exceptions 477 | 478 | The RuntimeException is thrown when some sort of external entity or environment causes a problem that is beyond your control providing the input is valid. This exception is the default case for when the cause of an error can't explicitly be determined. For example you tried to connect to a database but the database was not available (server down, etc). Another example might be if an SQL query failed. 479 | 480 | ##### UnexpectedValueException 481 | 482 | This type of exception should be used when an unexpected result is encountered. For example a function call returned a string when a boolean was expected. 483 | 484 | ##### OutOfBoundsException 485 | 486 | This exception has few practical applications but may be thrown if a value is not a valid key. 487 | 488 | ##### OverflowException 489 | 490 | This exception has few practical applications but may be thrown when you add an element into a full container. 491 | 492 | ##### RangeException 493 | 494 | This exception has few practical applications but may be thrown to indicate range errors during program execution. Normally this means there was an arithmetic error other than under/overflow. This is the runtime version of DomainException. 495 | 496 | ##### UnderflowException 497 | 498 | This exception has few practical applications but may thrown when you try to remove an element of an empty container. 499 | 500 | #### Documenting exceptions 501 | 502 | Each function or method must annotate the type of exception that it throws using an @throws tag and any downstream exceptions types that are thrown. Each type of exception need only be annotated once. No description is necessary. 503 | 504 | ### SQL Queries 505 | 506 | SQL keywords are to be written in uppercase, while all other identifiers (with the exception of quoted text obviously) is to be in lowercase. 507 | 508 | All table names should use the `#__` prefix to access Joomla content and allow for the user defined database prefix to be applied. Queries should also use the JDatabaseQuery API. Tables should never have a static prefix such as `jos_`. 509 | 510 | To query our data source we can call a number of JDatabaseQuery methods; these methods encapsulate the data source's query language (in most cases SQL), hiding query-specific syntax from the developer and increasing the portability of the developer's source code. 511 | 512 | Use Query chaining to connect a number of query methods, one after the other, with each method returning an object that can support the next method, This improves readability and simplifies the resulting code. Since the Joomla Framework was introduced "query chaining" is now the recommended method for building database queries. 513 | 514 | Table names and table column names should always be enclosed in the `quoteName()` method to escape the table name and table columns. 515 | Field values checked in a query should always be enclosed in the `quote()` method to escape the value before passing it to the database. Integer field values checked in a query should also be type cast to `(int)`. 516 | 517 | ```php 518 | // Get the database connector. 519 | $db = JFactory::getDbo(); 520 | 521 | // Get the query from the database connector. 522 | $query = $db->getQuery(true); 523 | 524 | // Build the query programatically (example with chaining) 525 | // Note: You can use the qn alias for the quoteName method. 526 | $query->select($db->qn('u.*')) 527 | ->from($db->qn('#__users', 'u')); 528 | 529 | // Tell the database connector what query to run. 530 | $db->setQuery($query); 531 | 532 | // Invoke the query or data retrieval helper. 533 | $users = $db->loadObjectList(); 534 | ``` 535 | 536 | #### Longer Chaining Example 537 | ```php 538 | // Using chaining when possible. 539 | $query->select($db->quoteName(array('user_id', 'profile_key', 'profile_value', 'ordering'))) 540 | ->from($db->quoteName('#__user_profiles')) 541 | ->where($db->quoteName('profile_key') . ' LIKE '. $db->quote('\'custom.%\'')) 542 | ->order('ordering ASC'); 543 | ``` 544 | 545 | #### Chaining With Select Array And Type Casting Of Integer Field Value Example 546 | ```php 547 | $query = $db->getQuery(true) 548 | ->select(array( 549 | $db->quoteName('profile_key'), 550 | $db->quoteName('profile_value'), 551 | )) 552 | ->from($db->quoteName('#__user_profiles')) 553 | ->where($db->quoteName('user_id') . ' = ' . (int) $userId) 554 | ->order($db->quoteName('ordering')); 555 | ``` 556 | -------------------------------------------------------------------------------- /manual/scss.md: -------------------------------------------------------------------------------- 1 | These guidelines have been assembled following an examination of emerging practices, ideas and existing styleguides, namely: 2 | 3 | 1. [Code Guide](http://codeguide.co) (by @mdo) 4 | 5 | ### Commenting 6 | 7 | #### Major sections 8 | Major code sections should be named in caps and within a full comment block, eg: 9 | 10 | ```scss 11 | // Major comment 12 | // 13 | // Major comment description goes here 14 | // and continues here 15 | ``` 16 | 17 | #### Sub sections 18 | Subsections should be normally cased and within an open comment block. 19 | ```scss 20 | // 21 | // Sub section comment 22 | // 23 | ``` 24 | 25 | #### Basic comments 26 | ```scss 27 | // Basic comment 28 | ``` 29 | 30 | ### Class naming 31 | Use dashes to create compound class names: 32 | 33 | ```scss 34 | // Good - use dashes 35 | .compound-class-name {…} 36 | 37 | // Good - uses camelCase 38 | .compoundClassName {…} 39 | 40 | // Bad - uses underscores 41 | .compound_class_name {…} 42 | 43 | // Bad - does not use seperators 44 | .compoundclassname {…} 45 | ``` 46 | 47 | ### Indentation 48 | Rules should be indented with 2 spaces: 49 | 50 | ```scss 51 | // Good 52 | .example { 53 | color: #000; 54 | visibility: hidden; 55 | } 56 | 57 | // Bad - using tabs or 4 spaces 58 | .example { 59 | color: #000; 60 | visibility: hidden; 61 | } 62 | ``` 63 | 64 | SCSS should also be nested, with child selectors and rules indented again. Nested rules should also be spaced by one line: 65 | 66 | ```scss 67 | // Good 68 | .example { 69 | 70 | > li { 71 | float: none; 72 | 73 | + li { 74 | margin-top: 2px; 75 | margin-left: 0; 76 | } 77 | 78 | } 79 | 80 | } 81 | 82 | // Bad - nested rules indented with tab or 4 spaces 83 | .example { 84 | 85 | > li { 86 | float: none; 87 | 88 | + li { 89 | margin-top: 2px; 90 | margin-left: 0; 91 | } 92 | 93 | } 94 | 95 | } 96 | ``` 97 | 98 | ### Alignment 99 | The opening brace must be on the same line as the last selector and preceded by a space. The closing brace must be on its own line after the last property and be indented to the same level as the opening brace. 100 | 101 | ```scss 102 | // Good 103 | .example { 104 | color: #fff; 105 | } 106 | 107 | // Bad - closing brace is in the wrong place 108 | .example { 109 | color: #fff; 110 | } 111 | 112 | // Bad - opening brace missing space 113 | .example{ 114 | color: #fff; 115 | } 116 | ``` 117 | 118 | ### Declaration order 119 | Related property declarations should be grouped together following the order: 120 | 121 | 1. Positioning 122 | 2. Box model 123 | 3. Typographic 124 | 4. Visual 125 | 126 | ```scss 127 | // Good 128 | .example { 129 | // Positioning 130 | position: absolute; 131 | top: 0; 132 | right: 0; 133 | bottom: 0; 134 | left: 0; 135 | z-index: 100; 136 | 137 | // Box-model 138 | display: block; 139 | float: right; 140 | width: 100px; 141 | height: 100px; 142 | 143 | // Typography 144 | font-family: Arial, sans-serif; 145 | font-size: 14px; 146 | line-height: 1.2; 147 | color: #333; 148 | text-align: center; 149 | 150 | // Visual 151 | background-color: #f5f5f5; 152 | border: 1px solid #e5e5e5; 153 | border-radius: 3px; 154 | 155 | // Misc 156 | opacity: 1; 157 | } 158 | ``` 159 | 160 | Within each group, you'll also need to order the properties. If any mistakes are made, the compiler will notify you and provide the correct order 161 | 162 | ### Property Format 163 | Each property must be on its own line and indented one level. There should be no space before the colon and one space after. All properties must end with a semicolon. 164 | 165 | ```scss 166 | // Good 167 | .example { 168 | background: black; 169 | color: #fff; 170 | } 171 | 172 | // Bad - missing spaces after colons 173 | .example { 174 | background:black; 175 | color:#fff; 176 | } 177 | 178 | // Bad - missing last semicolon 179 | .example { 180 | background: black; 181 | color: #fff 182 | } 183 | ``` 184 | 185 | ### HEX values 186 | HEX values must be declared in lowercase and shorthand: 187 | 188 | ```scss 189 | // Good 190 | .example { 191 | color: #eee; 192 | } 193 | 194 | // Bad 195 | .example { 196 | color: #EEEEEE; 197 | } 198 | ``` 199 | 200 | ### Attribute selectors 201 | Always use double quotes around attribute selectors. 202 | 203 | ```scss 204 | // Good 205 | input[type="button"] { 206 | ... 207 | } 208 | 209 | // Bad - missing quotes 210 | input[type=button] { 211 | ... 212 | } 213 | 214 | // Bad - using single quote 215 | input[type='button'] { 216 | ... 217 | } 218 | ``` 219 | 220 | ### Zero value units 221 | Zero values should not carry units. 222 | 223 | ```scss 224 | // Good 225 | .example { 226 | padding: 0; 227 | } 228 | 229 | // Bad - uses units 230 | .example { 231 | padding: 0px; 232 | } 233 | ``` 234 | 235 | ### Prefixing properties 236 | There is no need to prefix properties, as this will be automatically taken care of when compiling your code 237 | 238 | ```scss 239 | // Good 240 | .example { 241 | transform: rotate(30px); 242 | } 243 | 244 | // Bad - uses prefix 245 | .example { 246 | -webkit-transform: rotate(30px); 247 | transform: rotate(30px); 248 | } 249 | ``` 250 | 251 | ### End of file 252 | The end of the SCSS file should always have a blank line 253 | 254 | ```scss 255 | .example { 256 | padding: 0; 257 | } 258 | <<< Leave empty line here 259 | ``` 260 | -------------------------------------------------------------------------------- /manual/source-code-management.md: -------------------------------------------------------------------------------- 1 | Before we start talking about what Joomla! code should look like, it is appropriate to look at how and where the source code is stored. All serious software projects, whether driven by an Open Source community or developed within a company for proprietary purposes will manage the source code is some sort of source or version management system. The Joomla! project uses a Distributed Version Control System (DVCS) called Git hosted at [GitHub](https://github.com). 2 | 3 | ### The Joomla! Framework 4 | 5 | The [Joomla! Framework](https://github.com/joomla-framework) is a PHP framework that is designed to serve as a foundation for not only web applications (like a CMS) but other types of software such as command line applications. The files that form the Joomla! Framework are stored in a Distributed Version Control System (DVCS) called Git hosted at [GitHub](https://github.com). 6 | 7 | You can learn about how to get the Joomla Framework source code from the GitHub organisation, https://github.com/joomla-framework. 8 | 9 | Because Git treats the concepts of file revision numbers differently than Subversion, the repository revision number is not required in files (that is, the `@version` tag is not necessary). 10 | 11 | ### The Joomla! CMS 12 | 13 | The [Joomla! CMS](https://github.com/joomla/joomla-cms) is a Content Management System (CMS) which enables you to build Web sites and powerful online applications. It's a free and OpenSource software, distributed under the GNU General Public License version 2 or later. The files that form the Joomla! CMS are stored in a Distributed Version Control System (DVCS) called Git hosted at [GitHub](https://github.com). 14 | 15 | You can learn about how to get the Joomla CMS source code from the Git repository, https://github.com/joomla/joomla-cms. 16 | 17 | Because Git treats the concepts of file revision numbers differently than Subversion, the repository revision number is not required in files (that is, the `@version` tag is not necessary). 18 | 19 | ### Compliance Tool 20 | 21 | The standards in this manual have been adopted across the Joomla project, including the [Joomla! Framework](https://github.com/joomla-framework), the [Joomla! CMS](https://github.com/joomla/joomla-cms) and any other applications or extensions maintained by the project. These standards apply to source code, tests and (where applicable) documentation. 22 | 23 | A custom Joomla sniff standard for PHP files is maintained by the Joomla! project and available from the [coding standards](https://github.com/joomla/coding-standards) repository. The Sniff is based on the standard outlined in this document. For more information about how code standards are enforced see the analysis appendix of the manual. For information on using the Sniff see the documentation stored in its repository. 24 | -------------------------------------------------------------------------------- /manual/version.md: -------------------------------------------------------------------------------- 1 | Version 1.0 -------------------------------------------------------------------------------- /manual/xml.md: -------------------------------------------------------------------------------- 1 | ### Attributes 2 | 3 | * Each attribute is on its own line. 4 | * The four attributes `name`, `type`, `label` and `description` should be written in this order and at the top of the element definition. 5 | 6 | ### Closing Elements 7 | 8 | Elements should be closed with the closing tag on a new line. 9 | 10 | #### Exception 11 | 12 | When the element only has few attributes, then the whole element can stay on the same line. A max line length of 100 characters is recommended for good reading. 13 | 14 | ### Examples 15 | 16 | #### Element is **empty**: 17 | ```xml 18 | 24 | ``` 25 | 26 | #### Element is **not empty**: 27 | ```xml 28 | 34 | 35 | 36 | 37 | 38 | 44 | 50 | 53 | 54 | ``` 55 | 56 | #### Element with only a few attributes 57 | 58 | ```xml 59 |
60 | ``` 61 | -------------------------------------------------------------------------------- /notes.txt: -------------------------------------------------------------------------------- 1 | ========== 2 | The Joomla coding-standards for PHP_CodeSniffer has been derived from the Generic, PEAR, Squiz, PSR2, and Zend standards. 3 | Differences are noted below. 4 | ========== 5 | 6 | ruleset.xml 7 | - Increased notional limit to 150 chars. 8 | - Include/exclude additional sniffs from various standards (see file for specifics) 9 | 10 | Classes/InstantiateNewClassesSniff.php 11 | Classes/MemberVarScopeSniff.php 12 | Classes/MethodScopeSniff.php 13 | Classes/StaticThisUsageSniff.php 14 | 15 | Commenting/ClassCommentSniff.php 16 | - Added tags to check (copyright, etc not required in classes). 17 | - @package tag optional. 18 | Commenting/FileCommentSniff.php 19 | - If not short description is provided then the blank line check is not done. 20 | - Removed check on subpackage name in processSubpackage 21 | - Removed space checking after @ tags in processTags 22 | - Allowed any characters to appear before the copyright year in processCopyrights 23 | - @package tag optional. 24 | Commenting/FunctionCommentSniff.php 25 | - Remove check for one space before var type. 26 | - Remove check for 1 space after the longest type. 27 | - Remove check for 1 space after the variable name. 28 | - Remove check for variable name alignment. 29 | - Remove check for variable comment alignment. 30 | Commenting/SingleCommentSniff.php 31 | 32 | ControlStructures/ControlSignatureSniff.php 33 | ControlStructures/ElseIfDeclarationSniff.php 34 | - based on PSR2.ControlStructures.ElseIfDeclaration 35 | - Changed from WARNING to ERROR 36 | ControlStructures/InlineControlStructureSniff.php 37 | - Changed from WARNING to ERROR 38 | - Added exception if the file is a layout (ie, under a /tmpl/ folder). 39 | ControlStructures/MultiLineConditionSniff.php 40 | ControlStructures/WhiteSpaceBeforeSniff.php 41 | 42 | Functions/FunctionCallSignatureSniff.php 43 | Functions/FunctionDeclarationSniff.php 44 | Functions/StatementNotFunctionSniff.php 45 | 46 | NamingConventions/ValidFunctionNameSniff.php 47 | NamingConventions/ValidVariableNameSniff.php 48 | 49 | PHP/LowerCaseConstantSniff.php 50 | - Based on Generic.PHP.LowerCaseConstantSniff 51 | 52 | WhiteSpace/CastSpacingSniff.php 53 | - Squiz.WhiteSpace.CastSpacing 54 | WhiteSpace/ConcatenationSpacingSniff.php 55 | WhiteSpace/ControlStructureSpacingSniff.php 56 | WhiteSpace/DisallowSpaceIndentSniff.php 57 | - Based on Generic.WhiteSpace.DisallowSpaceIndent 58 | WhiteSpace/MemberVarSpacingSniff.php 59 | - Based on Squiz.WhiteSpace.MemberVarSpacing 60 | WhiteSpace/ObjectOperatorIndentSniff.php 61 | - Based on PEAR.WhiteSpace.ObjectOperatorIndent 62 | WhiteSpace/OperatorSpacingSniff.php 63 | WhiteSpace/SemicolonSpacingSniff.php 64 | - Based on Squiz.WhiteSpace.SemicolonSpacing 65 | WhiteSpace/SpaceAfterCastSniff.php 66 | WhiteSpace/SuperfluousWhitespaceSniff.php 67 | --------------------------------------------------------------------------------