more updates yay

This commit is contained in:
Wyatt Miller 2018-05-21 12:29:14 -04:00
parent 168f2f2a54
commit 2a39739af0
1105 changed files with 115947 additions and 34 deletions

View File

@ -1,31 +0,0 @@
<head>
<title>Wyatt J. Miller - Home</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="style.css" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script> <script src="main.js"></script>
<script src="main.js"></script>
</head>
<body>
<?php require "header.php";?>
<div class="col-xs-12">
<div class="main-section">
<div class="about-background">
<h2>About</h2>
<p>Hi there everybody. I'm Wyatt Miller, a programmer, a gamer, and a college student.</p>
<p>I think I'll post my resume up here so I don't have to explain what I'm like. lol.</p>
<p>I've worked in places where there's flashing lights and fans blowing hot air and you are really chilled to the bone. That's right, I've worked in IT. Let me tell you, IT is not a walk in the park compared to other jobs out there. Sure, you are around like-minded people that enjoy your company and you get to mess with the latest and greatest of technologies, but when you get down to it, it is hard. Griping with angry or upset customers and clients while trying to help politely and effectively is very difficult. When I get home after a work day, I just wanna take a nap or binge watch anime till I fall asleep. For me, though, IT is really fun.</p>
<p>As a programmer, I always think of the big picture, cross platform. That's why my language of choice is either Python or Ruby. I would say Java too but Java is garbage, I'll talk about that later blog post. Yeah, I do program in C#, C and C++ before that, but that is more of futuristic approach to programming language as a whole with .NET Core. .NET Core is gift sent down from the gods, bringing with it cross platform-ability, ASP .NET Core, Visual Basic Core, and others. People should get on that shit ASAP. I know I am. Yeah, compiled languages are cool too but you have set up cross compiling and you have to grab a separate machine for testing (or a VM if your computer is beefy enough lol) and yeah, that's a mess, I don't toy with that. When I have an actual job, maybe I'll get a build server, I don't know. In college, I'm studying C#/.NET, HTML/CSS/JS, and MSSQL, in part of getting my CIT degree, which has been a bumpy ride, let me tell you.</p>
<p>Anime binge watching is past time of mine as I find the animation very different from, let's say Pixar and Sony, do their animation. Plus, story telling in anime is really thought out and blows my mind most times. The exceptions I can give is No Game No Life and One Punch Man because I predict what is going to happen at the end of each episode. But, criticisms aside, I like watching Japanese animation whenever get the chance.</p>
<p>Gaming is what I'm doing if I'm not programming. Those part of the whole PCMasterRace, I don't take part in it. I believe you make people fell bad for having a console when they prefer console. Yeah, I do game on a PC but I like console games as well. Anywhere from retro games made in the 80's/90's to AAA games, both PC and console, are fantastic. My favorite games are: FEZ, Doom, Undertale, Final Fantasy VI, and Super Mario World. Okay so you're probably disappointed that Final Fantasy VII and Super Mario 64 aren't on my best games list. If Final Fantasy VII had a better villain, it would beat VI. Super Mario World is a nostalgia trip for me every time I play since I started playing it when I was one. Sorry, my opinion would've been different if it wasn't for my uncle who had an SNES at the time of my infancy. Some people are complaining that Undertale is on my best games list as well. To sum it up, I had the most thrilling time with Undertale and I wasn't bombarded by spoilers like the people complaining. Suck it up. If you have a game you think I should try out, leave a comment and I'll try it out (except CoD, do NOT suggest CoD to me).</p>
<p>I'll be adding more to this page so more is going to fill your eyeholes! I'm the eyehole man, nobody takes my eyeholes (anyone that guess that reference is going to be my best friend lol). </p>
<p>Later.</p>
<p>If you want my resume, I'll be posting that soon along with my cover letter.</p>
</div>
</div>
</div>
<?php require "footer.php";?>
</body>

View File

@ -16,8 +16,8 @@
</div>
<nav class="nav nav-pills nav-justified">
<a class="nav-item nav-link" href="/">Home</a>
<a class="nav-item nav-link" href="assets/resume.pdf">About</a>
<a class="nav-item nav-link" href="https://github.com/wymillerlinux?tab=repositories">Projects</a>
<a class="nav-item nav-link" href="assets/resume.pdf" target="_blank">About</a>
<a class="nav-item nav-link" href="https://github.com/wymillerlinux?tab=repositories" target="_blank">Projects</a>
<a class="nav-item nav-link" href="mailto:wjmiller2016@gmail.com">Contact</a>
</nav>
</div>

View File

@ -18,7 +18,7 @@
<div class="main-section">
<div class="main-background">
<h2 class="main-background-header">Hi, I'm Wyatt Miller!</h2>
<p class="main-background-text">I am a web designer, a programmer, a gamer, a chillax-er, but most importantly, I am a go-getter.</p><br>
<p class="main-background-text">I am a web designer, a programmer, a gamer, but most importantly, I am a go-getter.</p><br>
<p class="main-background-text">There's nothing much here on the home page but if you click or tap on the buttons below my name, you can view all sorts of things about yours truly. Go explore!</p>
<p class="main-background-text"></p>
</div>

7
vendor/autoload.php vendored Normal file
View File

@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitec8ca158f5381d4eb342d82b4c00512c::getLoader();

1
vendor/bin/phpcbf vendored Symbolic link
View File

@ -0,0 +1 @@
../squizlabs/php_codesniffer/bin/phpcbf

1
vendor/bin/phpcs vendored Symbolic link
View File

@ -0,0 +1 @@
../squizlabs/php_codesniffer/bin/phpcs

445
vendor/composer/ClassLoader.php vendored Normal file
View File

@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath.'\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

56
vendor/composer/LICENSE vendored Normal file
View File

@ -0,0 +1,56 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Composer
Upstream-Contact: Jordi Boggiano <j.boggiano@seld.be>
Source: https://github.com/composer/composer
Files: *
Copyright: 2016, Nils Adermann <naderman@naderman.de>
2016, Jordi Boggiano <j.boggiano@seld.be>
License: Expat
Files: src/Composer/Util/TlsHelper.php
Copyright: 2016, Nils Adermann <naderman@naderman.de>
2016, Jordi Boggiano <j.boggiano@seld.be>
2013, Evan Coury <me@evancoury.com>
License: Expat and BSD-2-Clause
License: BSD-2-Clause
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
.
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
License: Expat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

9
vendor/composer/autoload_classmap.php vendored Normal file
View File

@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

9
vendor/composer/autoload_psr4.php vendored Normal file
View File

@ -0,0 +1,9 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

52
vendor/composer/autoload_real.php vendored Normal file
View File

@ -0,0 +1,52 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitec8ca158f5381d4eb342d82b4c00512c
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitec8ca158f5381d4eb342d82b4c00512c', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitec8ca158f5381d4eb342d82b4c00512c', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitec8ca158f5381d4eb342d82b4c00512c::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
return $loader;
}
}

15
vendor/composer/autoload_static.php vendored Normal file
View File

@ -0,0 +1,15 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitec8ca158f5381d4eb342d82b4c00512c
{
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
}, null, ClassLoader::class);
}
}

55
vendor/composer/installed.json vendored Normal file
View File

@ -0,0 +1,55 @@
[
{
"name": "squizlabs/php_codesniffer",
"version": "3.2.3",
"version_normalized": "3.2.3.0",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "4842476c434e375f9d3182ff7b89059583aa8b27"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/4842476c434e375f9d3182ff7b89059583aa8b27",
"reference": "4842476c434e375f9d3182ff7b89059583aa8b27",
"shasum": ""
},
"require": {
"ext-simplexml": "*",
"ext-tokenizer": "*",
"ext-xmlwriter": "*",
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"time": "2018-02-20T21:35:23+00:00",
"bin": [
"bin/phpcs",
"bin/phpcbf"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"installation-source": "dist",
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Greg Sherwood",
"role": "lead"
}
],
"description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
"homepage": "http://www.squizlabs.com/php-codesniffer",
"keywords": [
"phpcs",
"standards"
]
}
]

View File

@ -0,0 +1,5 @@
.travis.yml export-ignore
package.xml export-ignore
phpunit.xml.dist export-ignore
php5-testingConfig.ini export-ignore
php7-testingConfig.ini export-ignore

View File

@ -0,0 +1,6 @@
/CodeSniffer.conf
/phpcs.xml
/phpunit.xml
.idea/*
/vendor/
composer.lock

View File

@ -0,0 +1,13 @@
Contributing
-------------
Before you contribute code to PHP\_CodeSniffer, please make sure it conforms to the PHPCS coding standard and that the PHP\_CodeSniffer unit tests still pass. The easiest way to contribute is to work on a checkout of the repository, or your own fork, rather than an installed PEAR version. If you do this, you can run the following commands to check if everything is ready to submit:
cd PHP_CodeSniffer
php bin/phpcs
Which should display no coding standard errors. And then:
phpunit
Which should give you no failures or errors. You can ignore any skipped tests as these are for external tools.

View File

@ -0,0 +1,9 @@
<?php
$phpCodeSnifferConfig = array (
'default_standard' => 'PSR2',
'report_format' => 'summary',
'show_warnings' => '0',
'show_progress' => '1',
'report_width' => '120',
)
?>

View File

@ -0,0 +1,77 @@
## About
PHP\_CodeSniffer is a set of two PHP scripts; the main `phpcs` script that tokenizes PHP, JavaScript and CSS files to detect violations of a defined coding standard, and a second `phpcbf` script to automatically correct coding standard violations. PHP\_CodeSniffer is an essential development tool that ensures your code remains clean and consistent.
[![Build Status](https://travis-ci.org/squizlabs/PHP_CodeSniffer.svg?branch=phpcs-fixer)](https://travis-ci.org/squizlabs/PHP_CodeSniffer) [![Code consistency](http://squizlabs.github.io/PHP_CodeSniffer/analysis/squizlabs/PHP_CodeSniffer/grade.svg)](http://squizlabs.github.io/PHP_CodeSniffer/analysis/squizlabs/PHP_CodeSniffer) [![Join the chat at https://gitter.im/squizlabs/PHP_CodeSniffer](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/squizlabs/PHP_CodeSniffer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Requirements
PHP\_CodeSniffer requires PHP version 5.4.0 or greater, although individual sniffs may have additional requirements such as external applications and scripts. See the [Configuration Options manual page](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Configuration-Options) for a list of these requirements.
## Installation
The easiest way to get started with PHP\_CodeSniffer is to download the Phar files for each of the commands:
curl -OL https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar
php phpcs.phar -h
curl -OL https://squizlabs.github.io/PHP_CodeSniffer/phpcbf.phar
php phpcbf.phar -h
### Composer
If you use Composer, you can install PHP_CodeSniffer system-wide with the following command:
composer global require "squizlabs/php_codesniffer=*"
Make sure you have the composer bin dir in your PATH. The default value is `~/.composer/vendor/bin/`, but you can check the value that you need to use by running `composer global config bin-dir --absolute`.
Or alternatively, include a dependency for `squizlabs/php_codesniffer` in your `composer.json` file. For example:
```json
{
"require-dev": {
"squizlabs/php_codesniffer": "3.*"
}
}
```
You will then be able to run PHP_CodeSniffer from the vendor bin directory:
./vendor/bin/phpcs -h
./vendor/bin/phpcbf -h
### Phive
If you use Phive, you can install PHP_CodeSniffer as a project tool using the following commands:
phive install phpcs
phive install phpcbf
You will then be able to run PHP_CodeSniffer from the tools directory:
./tools/phpcs -h
./tools/phpcbf -h
### PEAR
If you use PEAR, you can install PHP\_CodeSniffer using the PEAR installer. This will make the `phpcs` and `phpcbf` commands immediately available for use. To install PHP\_CodeSniffer using the PEAR installer, first ensure you have [installed PEAR](http://pear.php.net/manual/en/installation.getting.php) and then run the following command:
pear install PHP_CodeSniffer
### Git Clone
You can also download the PHP\_CodeSniffer source and run the `phpcs` and `phpcbf` commands directly from the Git clone:
git clone https://github.com/squizlabs/PHP_CodeSniffer.git
cd PHP_CodeSniffer
php bin/phpcs -h
php bin/phpcbf -h
## Documentation
The documentation for PHP\_CodeSniffer is available on the [Github wiki](https://github.com/squizlabs/PHP_CodeSniffer/wiki).
## Issues
Bug reports and feature requests can be submitted on the [Github Issue Tracker](https://github.com/squizlabs/PHP_CodeSniffer/issues).
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for information.

View File

@ -0,0 +1,288 @@
<?php
/**
* Autoloads files for PHP_CodeSniffer and tracks what has been loaded.
*
* Due to different namespaces being used for custom coding standards,
* the autoloader keeps track of what class is loaded after a file is included,
* even if the file is ultimately included by another autoloader (such as composer).
*
* This allows PHP_CodeSniffer to request the class name after loading a class
* when it only knows the filename, without having to parse the file to find it.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer;
if (class_exists('PHP_CodeSniffer\Autoload', false) === false) {
class Autoload
{
/**
* The composer autoloader.
*
* @var Composer\Autoload\ClassLoader
*/
private static $composerAutoloader = null;
/**
* A mapping of file names to class names.
*
* @var array<string, string>
*/
private static $loadedClasses = [];
/**
* A mapping of class names to file names.
*
* @var array<string, string>
*/
private static $loadedFiles = [];
/**
* A list of additional directories to search during autoloading.
*
* This is typically a list of coding standard directories.
*
* @var string[]
*/
private static $searchPaths = [];
/**
* Loads a class.
*
* This method only loads classes that exist in the PHP_CodeSniffer namespace.
* All other classes are ignored and loaded by subsequent autoloaders.
*
* @param string $class The name of the class to load.
*
* @return bool
*/
public static function load($class)
{
// Include the composer autoloader if there is one, but re-register it
// so this autoloader runs before the composer one as we need to include
// all files so we can figure out what the class/interface/trait name is.
if (self::$composerAutoloader === null) {
// Make sure we don't try to load any of Composer's classes
// while the autoloader is being setup.
if (strpos($class, 'Composer\\') === 0) {
return;
}
if (strpos(__DIR__, 'phar://') !== 0
&& file_exists(__DIR__.'/../../autoload.php') === true
) {
self::$composerAutoloader = include __DIR__.'/../../autoload.php';
if (self::$composerAutoloader instanceof \Composer\Autoload\ClassLoader) {
self::$composerAutoloader->unregister();
self::$composerAutoloader->register();
} else {
// Something went wrong, so keep going without the autoloader
// although namespaced sniffs might error.
self::$composerAutoloader = false;
}
} else {
self::$composerAutoloader = false;
}
}//end if
$ds = DIRECTORY_SEPARATOR;
$path = false;
if (substr($class, 0, 16) === 'PHP_CodeSniffer\\') {
if (substr($class, 0, 22) === 'PHP_CodeSniffer\Tests\\') {
$isInstalled = !is_dir(__DIR__.$ds.'tests');
if ($isInstalled === false) {
$path = __DIR__.$ds.'tests';
} else {
$path = '@test_dir@'.$ds.'PHP_CodeSniffer'.$ds.'CodeSniffer';
}
$path .= $ds.substr(str_replace('\\', $ds, $class), 22).'.php';
} else {
$path = __DIR__.$ds.'src'.$ds.substr(str_replace('\\', $ds, $class), 16).'.php';
}
}
// See if the composer autoloader knows where the class is.
if ($path === false && self::$composerAutoloader !== false) {
$path = self::$composerAutoloader->findFile($class);
}
// See if the class is inside one of our alternate search paths.
if ($path === false) {
foreach (self::$searchPaths as $searchPath => $nsPrefix) {
$className = $class;
if ($nsPrefix !== '' && substr($class, 0, strlen($nsPrefix)) === $nsPrefix) {
$className = substr($class, (strlen($nsPrefix) + 1));
}
$path = $searchPath.$ds.str_replace('\\', $ds, $className).'.php';
if (is_file($path) === true) {
break;
}
$path = false;
}
}
if ($path !== false && is_file($path) === true) {
self::loadFile($path);
return true;
}
return false;
}//end load()
/**
* Includes a file and tracks what class or interface was loaded as a result.
*
* @param string $path The path of the file to load.
*
* @return string The fully qualified name of the class in the loaded file.
*/
public static function loadFile($path)
{
if (strpos(__DIR__, 'phar://') !== 0) {
$path = realpath($path);
if ($path === false) {
return false;
}
}
if (isset(self::$loadedClasses[$path]) === true) {
return self::$loadedClasses[$path];
}
$classes = get_declared_classes();
$interfaces = get_declared_interfaces();
$traits = get_declared_traits();
include $path;
$className = null;
$newClasses = array_reverse(array_diff(get_declared_classes(), $classes));
foreach ($newClasses as $name) {
if (isset(self::$loadedFiles[$name]) === false) {
$className = $name;
break;
}
}
if ($className === null) {
$newClasses = array_reverse(array_diff(get_declared_interfaces(), $interfaces));
foreach ($newClasses as $name) {
if (isset(self::$loadedFiles[$name]) === false) {
$className = $name;
break;
}
}
}
if ($className === null) {
$newClasses = array_reverse(array_diff(get_declared_traits(), $traits));
foreach ($newClasses as $name) {
if (isset(self::$loadedFiles[$name]) === false) {
$className = $name;
break;
}
}
}
self::$loadedClasses[$path] = $className;
self::$loadedFiles[$className] = $path;
return self::$loadedClasses[$path];
}//end loadFile()
/**
* Adds a directory to search during autoloading.
*
* @param string $path The path to the directory to search.
* @param string $nsPrefix The namespace prefix used by files under this path.
*
* @return void
*/
public static function addSearchPath($path, $nsPrefix='')
{
self::$searchPaths[$path] = rtrim(trim((string) $nsPrefix), '\\');
}//end addSearchPath()
/**
* Gets the class name for the given file path.
*
* @param string $path The name of the file.
*
* @throws \Exception If the file path has not been loaded.
* @return string
*/
public static function getLoadedClassName($path)
{
if (isset(self::$loadedClasses[$path]) === false) {
throw new \Exception("Cannot get class name for $path; file has not been included");
}
return self::$loadedClasses[$path];
}//end getLoadedClassName()
/**
* Gets the file path for the given class name.
*
* @param string $class The name of the class.
*
* @throws \Exception If the class name has not been loaded
* @return string
*/
public static function getLoadedFileName($class)
{
if (isset(self::$loadedFiles[$class]) === false) {
throw new \Exception("Cannot get file name for $class; class has not been included");
}
return self::$loadedFiles[$class];
}//end getLoadedFileName()
/**
* Gets the mapping of file names to class names.
*
* @return array<string, string>
*/
public static function getLoadedClasses()
{
return self::$loadedClasses;
}//end getLoadedClasses()
/**
* Gets the mapping of class names to file names.
*
* @return array<string, string>
*/
public static function getLoadedFiles()
{
return self::$loadedFiles;
}//end getLoadedFiles()
}//end class
// Register the autoloader before any existing autoloaders to ensure
// it gets a chance to hear about every autoload request, and record
// the file and class name for it.
spl_autoload_register(__NAMESPACE__.'\Autoload::load', true, true);
}//end if

19
vendor/squizlabs/php_codesniffer/bin/phpcbf vendored Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env php
<?php
/**
* PHP Code Beautifier and Fixer fixes violations of a defined coding standard.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
if (is_file(__DIR__.'/../autoload.php') === true) {
include_once __DIR__.'/../autoload.php';
} else {
include_once 'PHP/CodeSniffer/autoload.php';
}
$runner = new PHP_CodeSniffer\Runner();
$exitCode = $runner->runPHPCBF();
exit($exitCode);

View File

@ -0,0 +1,12 @@
@echo off
REM PHP Code Beautifier and Fixer fixes violations of a defined coding standard.
REM
REM @author Greg Sherwood <gsherwood@squiz.net>
REM @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
REM @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
if "%PHP_PEAR_PHP_BIN%" neq "" (
set PHPBIN=%PHP_PEAR_PHP_BIN%
) else set PHPBIN=php
"%PHPBIN%" "%~dp0\phpcbf" %*

19
vendor/squizlabs/php_codesniffer/bin/phpcs vendored Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env php
<?php
/**
* PHP_CodeSniffer detects violations of a defined coding standard.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
if (is_file(__DIR__.'/../autoload.php') === true) {
include_once __DIR__.'/../autoload.php';
} else {
include_once 'PHP/CodeSniffer/autoload.php';
}
$runner = new PHP_CodeSniffer\Runner();
$exitCode = $runner->runPHPCS();
exit($exitCode);

View File

@ -0,0 +1,12 @@
@echo off
REM PHP_CodeSniffer detects violations of a defined coding standard.
REM
REM @author Greg Sherwood <gsherwood@squiz.net>
REM @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
REM @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
if "%PHP_PEAR_PHP_BIN%" neq "" (
set PHPBIN=%PHP_PEAR_PHP_BIN%
) else set PHPBIN=php
"%PHPBIN%" "%~dp0\phpcs" %*

View File

@ -0,0 +1,40 @@
{
"name": "squizlabs/php_codesniffer",
"description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
"type": "library",
"keywords": [
"phpcs",
"standards"
],
"homepage": "http://www.squizlabs.com/php-codesniffer",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Greg Sherwood",
"role": "lead"
}
],
"support": {
"issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
"wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki",
"source": "https://github.com/squizlabs/PHP_CodeSniffer"
},
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"require": {
"php": ">=5.4.0",
"ext-tokenizer": "*",
"ext-xmlwriter": "*",
"ext-simplexml": "*"
},
"require-dev": {
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"bin": [
"bin/phpcs",
"bin/phpcbf"
]
}

View File

@ -0,0 +1,24 @@
Copyright (c) 2012, Squiz Pty Ltd (ABN 77 084 670 600)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Squiz Pty Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,121 @@
<?xml version="1.0"?>
<ruleset name="PHP_CodeSniffer">
<description>The coding standard for PHP_CodeSniffer itself.</description>
<file>autoload.php</file>
<file>bin</file>
<file>src</file>
<file>tests</file>
<exclude-pattern>*/Standards/*/Tests/*\.(inc|css|js)</exclude-pattern>
<arg name="basepath" value="."/>
<arg name="colors" />
<arg name="parallel" value="75" />
<arg value="np"/>
<!-- Don't hide tokenizer exceptions -->
<rule ref="Internal.Tokenizer.Exception">
<type>error</type>
</rule>
<!-- Include the whole PEAR standard -->
<rule ref="PEAR">
<exclude name="PEAR.NamingConventions.ValidFunctionName" />
<exclude name="PEAR.NamingConventions.ValidVariableName" />
<exclude name="PEAR.Commenting.ClassComment" />
<exclude name="PEAR.Commenting.FileComment.MissingCategoryTag" />
<exclude name="PEAR.Commenting.FileComment.MissingPackageTag" />
<exclude name="PEAR.Commenting.FileComment.MissingLinkTag" />
<exclude name="PEAR.Commenting.FileComment.MissingVersion" />
</rule>
<!-- Include some sniffs from other standards that don't conflict with PEAR -->
<rule ref="Squiz.Arrays.ArrayBracketSpacing" />
<rule ref="Squiz.Arrays.ArrayDeclaration" />
<rule ref="Squiz.Commenting.ClosingDeclarationComment" />
<rule ref="Squiz.ControlStructures.ControlSignature" />
<rule ref="Squiz.ControlStructures.ElseIfDeclaration" />
<rule ref="Squiz.Commenting.BlockComment" />
<rule ref="Squiz.Commenting.DocCommentAlignment" />
<rule ref="Squiz.Commenting.EmptyCatchComment" />
<rule ref="Squiz.Commenting.InlineComment" />
<rule ref="Squiz.Commenting.LongConditionClosingComment" />
<rule ref="Squiz.Commenting.PostStatementComment" />
<rule ref="Squiz.Commenting.VariableComment" />
<rule ref="Squiz.Formatting.OperatorBracket" />
<rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing" />
<rule ref="Squiz.Operators.ComparisonOperatorUsage" />
<rule ref="Squiz.PHP.DisallowInlineIf" />
<rule ref="Squiz.Strings.ConcatenationSpacing" />
<rule ref="Squiz.WhiteSpace.ControlStructureSpacing" />
<rule ref="Squiz.WhiteSpace.FunctionClosingBraceSpace" />
<rule ref="Squiz.WhiteSpace.FunctionSpacing" />
<rule ref="Squiz.WhiteSpace.OperatorSpacing" />
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace" />
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
<rule ref="Generic.Commenting.Todo"/>
<rule ref="Generic.ControlStructures.InlineControlStructure"/>
<rule ref="Generic.Formatting.DisallowMultipleStatements"/>
<rule ref="Generic.Formatting.SpaceAfterCast"/>
<rule ref="Generic.NamingConventions.ConstructorName"/>
<rule ref="Generic.PHP.DeprecatedFunctions"/>
<rule ref="Generic.PHP.LowerCaseKeyword"/>
<rule ref="Generic.Strings.UnnecessaryStringConcat"/>
<rule ref="PSR2.Classes.PropertyDeclaration"/>
<rule ref="PSR2.Methods.MethodDeclaration"/>
<rule ref="PSR2.Files.EndFileNewline"/>
<rule ref="Zend.Files.ClosingTag"/>
<!-- We use custom indent rules for arrays -->
<rule ref="Generic.Arrays.ArrayIndent"/>
<rule ref="Squiz.Arrays.ArrayDeclaration.KeyNotAligned">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.ValueNotAligned">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.CloseBraceNotAligned">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.CloseBraceNewLine">
<severity>0</severity>
</rule>
<!-- Check var names, but we don't want leading underscores for private vars -->
<rule ref="Squiz.NamingConventions.ValidVariableName" />
<rule ref="Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore">
<severity>0</severity>
</rule>
<!-- Only one argument per line in multi-line function calls -->
<rule ref="PEAR.Functions.FunctionCallSignature">
<properties>
<property name="allowMultipleArguments" value="false"/>
</properties>
</rule>
<!-- Have 12 chars padding maximum and always show as errors -->
<rule ref="Generic.Formatting.MultipleStatementAlignment">
<properties>
<property name="maxPadding" value="12"/>
<property name="error" value="true"/>
</properties>
</rule>
<!-- Private methods MUST not be prefixed with an underscore -->
<rule ref="PSR2.Methods.MethodDeclaration.Underscore">
<type>error</type>
</rule>
<!-- Private properties MUST not be prefixed with an underscore -->
<rule ref="PSR2.Classes.PropertyDeclaration.Underscore">
<type>error</type>
</rule>
<!-- The testing bootstrap file uses string concats to stop IDEs seeing the class aliases -->
<rule ref="Generic.Strings.UnnecessaryStringConcat">
<exclude-pattern>tests/bootstrap.php</exclude-pattern>
</rule>
</ruleset>

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="qualified">
<xs:element name="ruleset">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="description" type="xs:string" maxOccurs="1" minOccurs="0"></xs:element>
<xs:element name="config" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:attribute name="name" type="xs:string" use="required"></xs:attribute>
<xs:attribute name="value" type="xs:string" use="required"></xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="file" type="xs:string" maxOccurs="unbounded" minOccurs="0"></xs:element>
<xs:element name="exclude-pattern" type="patternType" maxOccurs="unbounded" minOccurs="0"></xs:element>
<xs:element name="arg" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:attribute name="name" type="xs:string"></xs:attribute>
<xs:attribute name="value" type="xs:string"></xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="ini" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:attribute name="name" type="xs:string" use="required"></xs:attribute>
<xs:attribute name="value" type="xs:string" use="required"></xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="autoload" type="xs:string" maxOccurs="unbounded" minOccurs="0"></xs:element>
<xs:element name="rule" type="ruleType" maxOccurs="unbounded" minOccurs="0"></xs:element>
</xs:choice>
<xs:attribute name="name" type="xs:string"></xs:attribute>
<xs:attribute name="namespace" type="xs:string"></xs:attribute>
</xs:complexType>
</xs:element>
<xs:complexType name="ruleType">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="exclude" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:attribute name="name" type="xs:string" use="required"></xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="message" type="xs:string" maxOccurs="1" minOccurs="0"></xs:element>
<xs:element name="severity" type="xs:integer" maxOccurs="1" minOccurs="0"></xs:element>
<xs:element name="type" maxOccurs="1" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="error"></xs:enumeration>
<xs:enumeration value="warning"></xs:enumeration>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="exclude-pattern" type="patternType" maxOccurs="unbounded" minOccurs="0"></xs:element>
<xs:element name="include-pattern" type="patternType" maxOccurs="unbounded" minOccurs="0"></xs:element>
<xs:element name="properties" type="propertiesType" maxOccurs="1" minOccurs="0"></xs:element>
</xs:choice>
<xs:attribute name="ref" type="xs:string" use="required"></xs:attribute>
</xs:complexType>
<xs:complexType name="patternType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="type">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="relative"></xs:enumeration>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="propertiesType">
<xs:sequence>
<xs:element name="property" maxOccurs="unbounded" minOccurs="1">
<xs:complexType>
<xs:attribute name="type">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="array"></xs:enumeration>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="name" type="xs:string" use="required"></xs:attribute>
<xs:attribute name="value" type="xs:string" use="required"></xs:attribute>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,96 @@
#!/usr/bin/env php
<?php
/**
* Build a PHPCS phar.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Benjamin Pearson <bpearson@squiz.com.au>
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
error_reporting(E_ALL | E_STRICT);
if (ini_get('phar.readonly') === '1') {
echo 'Unable to build, phar.readonly in php.ini is set to read only.'.PHP_EOL;
exit(1);
}
$scripts = array(
'phpcs',
'phpcbf',
);
foreach ($scripts as $script) {
echo "Building $script phar".PHP_EOL;
$pharName = $script.'.phar';
$pharFile = getcwd().'/'.$pharName;
echo "\t=> $pharFile".PHP_EOL;
if (file_exists($pharFile) === true) {
echo "\t** file exists, removing **".PHP_EOL;
unlink($pharFile);
}
$phar = new Phar($pharFile, 0, $pharName);
/*
Add the files.
*/
echo "\t=> adding files... ";
$srcDir = realpath(__DIR__.'/../src');
$srcDirLen = strlen($srcDir);
$rdi = new \RecursiveDirectoryIterator($srcDir, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
$di = new \RecursiveIteratorIterator($rdi, 0, \RecursiveIteratorIterator::CATCH_GET_CHILD);
foreach ($di as $file) {
$filename = $file->getFilename();
// Skip hidden files.
if (substr($filename, 0, 1) === '.') {
continue;
}
$fullpath = $file->getPathname();
if (strpos($fullpath, '/Tests/') !== false) {
continue;
}
$path = 'src'.substr($fullpath, $srcDirLen);
$phar->addFromString($path, php_strip_whitespace($fullpath));
}
// Add autoloader.
$phar->addFromString('autoload.php', php_strip_whitespace(realpath(__DIR__.'/../autoload.php')));
// Add licence file.
$phar->addFromString('licence.txt', php_strip_whitespace(realpath(__DIR__.'/../licence.txt')));
echo 'done'.PHP_EOL;
/*
Add the stub.
*/
echo "\t=> adding stub... ";
$stub = '#!/usr/bin/env php'."\n";
$stub .= '<?php'."\n";
$stub .= 'Phar::mapPhar(\''.$pharName.'\');'."\n";
$stub .= 'require_once "phar://'.$pharName.'/autoload.php";'."\n";
$stub .= '$runner = new PHP_CodeSniffer\Runner();'."\n";
$stub .= '$exitCode = $runner->run'.$script.'();'."\n";
$stub .= 'exit($exitCode);'."\n";
$stub .= '__HALT_COMPILER();';
$phar->setStub($stub);
echo 'done'.PHP_EOL;
}//end foreach

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
<?php
/**
* An exception thrown by PHP_CodeSniffer when it wants to exit from somewhere not in the main runner.
*
* Allows the runner to return an exit code instead of putting exit codes elsewhere
* in the source code.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Exceptions;
class DeepExitException extends \Exception
{
}//end class

View File

@ -0,0 +1,15 @@
<?php
/**
* An exception thrown by PHP_CodeSniffer when it encounters an unrecoverable error.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Exceptions;
class RuntimeException extends \Exception
{
}//end class

View File

@ -0,0 +1,15 @@
<?php
/**
* An exception thrown by PHP_CodeSniffer when it encounters an unrecoverable tokenizer error.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Exceptions;
class TokenizerException extends \Exception
{
}//end class

View File

@ -0,0 +1,82 @@
<?php
/**
* A dummy file represents a chunk of text that does not have a file system location.
*
* Dummy files can also represent a changed (but not saved) version of a file
* and so can have a file path either set manually, or set by putting
* phpcs_input_file: /path/to/file
* as the first line of the file contents.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Files;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Config;
class DummyFile extends File
{
/**
* Creates a DummyFile object and sets the content.
*
* @param string $content The content of the file.
* @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
* @param \PHP_CodeSniffer\Config $config The config data for the run.
*
* @return void
*/
public function __construct($content, Ruleset $ruleset, Config $config)
{
$this->setContent($content);
// See if a filename was defined in the content.
// This is done by including: phpcs_input_file: [file path]
// as the first line of content.
$path = 'STDIN';
if ($content !== null) {
if (substr($content, 0, 17) === 'phpcs_input_file:') {
$eolPos = strpos($content, $this->eolChar);
$filename = trim(substr($content, 17, ($eolPos - 17)));
$content = substr($content, ($eolPos + strlen($this->eolChar)));
$path = $filename;
$this->setContent($content);
}
}
// The CLI arg overrides anything passed in the content.
if ($config->stdinPath !== null) {
$path = $config->stdinPath;
}
return parent::__construct($path, $ruleset, $config);
}//end __construct()
/**
* Set the error, warning, and fixable counts for the file.
*
* @param int $errorCount The number of errors found.
* @param int $warningCount The number of warnings found.
* @param int $fixableCount The number of fixable errors found.
* @param int $fixedCount The number of errors that were fixed.
*
* @return void
*/
public function setErrorCounts($errorCount, $warningCount, $fixableCount, $fixedCount)
{
$this->errorCount = $errorCount;
$this->warningCount = $warningCount;
$this->fixableCount = $fixableCount;
$this->fixedCount = $fixedCount;
}//end setErrorCounts()
}//end class

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,246 @@
<?php
/**
* Represents a list of files on the file system that are to be checked during the run.
*
* File objects are created as needed rather than all at once.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Files;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Exceptions\DeepExitException;
class FileList implements \Iterator, \Countable
{
/**
* A list of file paths that are included in the list.
*
* @var array
*/
private $files = [];
/**
* The number of files in the list.
*
* @var integer
*/
private $numFiles = 0;
/**
* The config data for the run.
*
* @var \PHP_CodeSniffer\Config
*/
public $config = null;
/**
* The ruleset used for the run.
*
* @var \PHP_CodeSniffer\Ruleset
*/
public $ruleset = null;
/**
* An array of patterns to use for skipping files.
*
* @var array
*/
protected $ignorePatterns = [];
/**
* Constructs a file list and loads in an array of file paths to process.
*
* @param \PHP_CodeSniffer\Config $config The config data for the run.
* @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
*
* @return void
*/
public function __construct(Config $config, Ruleset $ruleset)
{
$this->ruleset = $ruleset;
$this->config = $config;
$paths = $config->files;
foreach ($paths as $path) {
$isPharFile = Util\Common::isPharFile($path);
if (is_dir($path) === true || $isPharFile === true) {
if ($isPharFile === true) {
$path = 'phar://'.$path;
}
$filterClass = $this->getFilterClass();
$di = new \RecursiveDirectoryIterator($path, (\RecursiveDirectoryIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS));
$filter = new $filterClass($di, $path, $config, $ruleset);
$iterator = new \RecursiveIteratorIterator($filter);
foreach ($iterator as $file) {
$this->files[$file->getPathname()] = null;
$this->numFiles++;
}
} else {
$this->addFile($path);
}//end if
}//end foreach
reset($this->files);
}//end __construct()
/**
* Add a file to the list.
*
* If a file object has already been created, it can be passed here.
* If it is left NULL, it will be created when accessed.
*
* @param string $path The path to the file being added.
* @param \PHP_CodeSniffer\Files\File $file The file being added.
*
* @return void
*/
public function addFile($path, $file=null)
{
// No filtering is done for STDIN when the filename
// has not been specified.
if ($path === 'STDIN') {
$this->files[$path] = $file;
$this->numFiles++;
return;
}
$filterClass = $this->getFilterClass();
$di = new \RecursiveArrayIterator([$path]);
$filter = new $filterClass($di, $path, $this->config, $this->ruleset);
$iterator = new \RecursiveIteratorIterator($filter);
foreach ($iterator as $path) {
$this->files[$path] = $file;
$this->numFiles++;
}
}//end addFile()
/**
* Get the class name of the filter being used for the run.
*
* @return string
*/
private function getFilterClass()
{
$filterType = $this->config->filter;
if ($filterType === null) {
$filterClass = '\PHP_CodeSniffer\Filters\Filter';
} else {
if (strpos($filterType, '.') !== false) {
// This is a path to a custom filter class.
$filename = realpath($filterType);
if ($filename === false) {
$error = "ERROR: Custom filter \"$filterType\" not found".PHP_EOL;
throw new DeepExitException($error, 3);
}
$filterClass = \PHP_CodeSniffer\Autoload::loadFile($filename);
} else {
$filterClass = '\PHP_CodeSniffer\Filters\\'.$filterType;
}
}
return $filterClass;
}//end getFilterClass()
/**
* Rewind the iterator to the first file.
*
* @return void
*/
function rewind()
{
reset($this->files);
}//end rewind()
/**
* Get the file that is currently being processed.
*
* @return \PHP_CodeSniffer\Files\File
*/
function current()
{
$path = key($this->files);
if ($this->files[$path] === null) {
$this->files[$path] = new LocalFile($path, $this->ruleset, $this->config);
}
return $this->files[$path];
}//end current()
/**
* Return the file path of the current file being processed.
*
* @return void
*/
function key()
{
return key($this->files);
}//end key()
/**
* Move forward to the next file.
*
* @return void
*/
function next()
{
next($this->files);
}//end next()
/**
* Checks if current position is valid.
*
* @return boolean
*/
function valid()
{
if (current($this->files) === false) {
return false;
}
return true;
}//end valid()
/**
* Return the number of files in the list.
*
* @return integer
*/
function count()
{
return $this->numFiles;
}//end count()
}//end class

View File

@ -0,0 +1,212 @@
<?php
/**
* A local file represents a chunk of text has a file system location.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Files;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Util\Cache;
class LocalFile extends File
{
/**
* Creates a LocalFile object and sets the content.
*
* @param string $path The absolute path to the file.
* @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
* @param \PHP_CodeSniffer\Config $config The config data for the run.
*
* @return void
*/
public function __construct($path, Ruleset $ruleset, Config $config)
{
$this->path = trim($path);
if (is_readable($this->path) === false) {
parent::__construct($this->path, $ruleset, $config);
$error = 'Error opening file; file no longer exists or you do not have access to read the file';
$this->addMessage(true, $error, 1, 1, 'Internal.LocalFile', [], 5, false);
$this->ignored = true;
return;
}
// Before we go and spend time tokenizing this file, just check
// to see if there is a tag up top to indicate that the whole
// file should be ignored. It must be on one of the first two lines.
if ($config->annotations === true) {
$handle = fopen($this->path, 'r');
if ($handle !== false) {
$firstContent = fgets($handle);
$firstContent .= fgets($handle);
fclose($handle);
if (strpos($firstContent, '@codingStandardsIgnoreFile') !== false
|| strpos(strtolower($firstContent), 'phpcs:ignorefile') !== false
) {
// We are ignoring the whole file.
$this->ignored = true;
return;
}
}
}
$this->reloadContent();
return parent::__construct($this->path, $ruleset, $config);
}//end __construct()
/**
* Loads the latest version of the file's content from the file system.
*
* @return void
*/
function reloadContent()
{
$this->setContent(file_get_contents($this->path));
}//end reloadContent()
/**
* Processes the file.
*
* @return void
*/
public function process()
{
if ($this->ignored === true) {
return;
}
if ($this->configCache['cache'] === false) {
return parent::process();
}
$hash = md5_file($this->path);
$cache = Cache::get($this->path);
if ($cache !== false && $cache['hash'] === $hash) {
// We can't filter metrics, so just load all of them.
$this->metrics = $cache['metrics'];
if ($this->configCache['recordErrors'] === true) {
// Replay the cached errors and warnings to filter out the ones
// we don't need for this specific run.
$this->configCache['cache'] = false;
$this->replayErrors($cache['errors'], $cache['warnings']);
$this->configCache['cache'] = true;
} else {
$this->errorCount = $cache['errorCount'];
$this->warningCount = $cache['warningCount'];
$this->fixableCount = $cache['fixableCount'];
}
if (PHP_CODESNIFFER_VERBOSITY > 0
|| (PHP_CODESNIFFER_CBF === true && empty($this->config->files) === false)
) {
echo "[loaded from cache]... ";
}
$this->numTokens = $cache['numTokens'];
$this->fromCache = true;
return;
}//end if
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo PHP_EOL;
}
parent::process();
$cache = [
'hash' => $hash,
'errors' => $this->errors,
'warnings' => $this->warnings,
'metrics' => $this->metrics,
'errorCount' => $this->errorCount,
'warningCount' => $this->warningCount,
'fixableCount' => $this->fixableCount,
'numTokens' => $this->numTokens,
];
Cache::set($this->path, $cache);
// During caching, we don't filter out errors in any way, so
// we need to do that manually now by replaying them.
if ($this->configCache['recordErrors'] === true) {
$this->configCache['cache'] = false;
$this->replayErrors($this->errors, $this->warnings);
$this->configCache['cache'] = true;
}
}//end process()
/**
* Clears and replays error and warnings for the file.
*
* Replaying errors and warnings allows for filtering rules to be changed
* and then errors and warnings to be reapplied with the new rules. This is
* particularly useful while caching.
*
* @param array $errors The list of errors to replay.
* @param array $warnings The list of warnings to replay.
*
* @return void
*/
private function replayErrors($errors, $warnings)
{
$this->errors = [];
$this->warnings = [];
$this->errorCount = 0;
$this->warningCount = 0;
$this->fixableCount = 0;
foreach ($errors as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$this->activeListener = $error['listener'];
$this->addMessage(
true,
$error['message'],
$line,
$column,
$error['source'],
[],
$error['severity'],
$error['fixable']
);
}
}
}
foreach ($warnings as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$this->activeListener = $error['listener'];
$this->addMessage(
false,
$error['message'],
$line,
$column,
$error['source'],
[],
$error['severity'],
$error['fixable']
);
}
}
}
}//end replayErrors()
}//end class

View File

@ -0,0 +1,108 @@
<?php
/**
* An abstract filter class for checking files and folders against exact matches.
*
* Supports both whitelists and blacklists.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Filters;
use PHP_CodeSniffer\Util;
abstract class ExactMatch extends Filter
{
/**
* A list of files to exclude.
*
* @var array
*/
private $blacklist = null;
/**
* A list of files to include.
*
* If the whitelist is empty, only files in the blacklist will be excluded.
*
* @var array
*/
private $whitelist = null;
/**
* Check whether the current element of the iterator is acceptable.
*
* If a file is both blacklisted and whitelisted, it will be deemed unacceptable.
*
* @return bool
*/
public function accept()
{
if (parent::accept() === false) {
return false;
}
if ($this->blacklist === null) {
$this->blacklist = $this->getblacklist();
}
if ($this->whitelist === null) {
$this->whitelist = $this->getwhitelist();
}
$filePath = Util\Common::realpath($this->current());
// If file is both blacklisted and whitelisted, the blacklist takes precedence.
if (isset($this->blacklist[$filePath]) === true) {
return false;
}
if (empty($this->whitelist) === true && empty($this->blacklist) === false) {
// We are only checking a blacklist, so everything else should be whitelisted.
return true;
}
return isset($this->whitelist[$filePath]);
}//end accept()
/**
* Returns an iterator for the current entry.
*
* Ensures that the blacklist and whitelist are preserved so they don't have
* to be generated each time.
*
* @return \RecursiveIterator
*/
public function getChildren()
{
$children = parent::getChildren();
$children->blacklist = $this->blacklist;
$children->whitelist = $this->whitelist;
return $children;
}//end getChildren()
/**
* Get a list of blacklisted file paths.
*
* @return array
*/
abstract protected function getBlacklist();
/**
* Get a list of whitelisted file paths.
*
* @return array
*/
abstract protected function getWhitelist();
}//end class

View File

@ -0,0 +1,269 @@
<?php
/**
* A base filter class for filtering out files and folders during a run.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Filters;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Config;
class Filter extends \RecursiveFilterIterator
{
/**
* The top-level path we are filtering.
*
* @var string
*/
protected $basedir = null;
/**
* The config data for the run.
*
* @var \PHP_CodeSniffer\Config
*/
protected $config = null;
/**
* The ruleset used for the run.
*
* @var \PHP_CodeSniffer\Ruleset
*/
protected $ruleset = null;
/**
* A list of ignore patterns that apply to directories only.
*
* @var array
*/
protected $ignoreDirPatterns = null;
/**
* A list of ignore patterns that apply to files only.
*
* @var array
*/
protected $ignoreFilePatterns = null;
/**
* A list of file paths we've already accepted.
*
* Used to ensure we aren't following circular symlinks.
*
* @var array
*/
protected $acceptedPaths = [];
/**
* Constructs a filter.
*
* @param \RecursiveIterator $iterator The iterator we are using to get file paths.
* @param string $basedir The top-level path we are filtering.
* @param \PHP_CodeSniffer\Config $config The config data for the run.
* @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
*
* @return void
*/
public function __construct($iterator, $basedir, Config $config, Ruleset $ruleset)
{
parent::__construct($iterator);
$this->basedir = $basedir;
$this->config = $config;
$this->ruleset = $ruleset;
}//end __construct()
/**
* Check whether the current element of the iterator is acceptable.
*
* Files are checked for allowed extensions and ignore patterns.
* Directories are checked for ignore patterns only.
*
* @return bool
*/
public function accept()
{
$filePath = $this->current();
$realPath = Util\Common::realpath($filePath);
if ($realPath !== false) {
// It's a real path somewhere, so record it
// to check for circular symlinks.
if (isset($this->acceptedPaths[$realPath]) === true) {
// We've been here before.
return false;
}
}
$filePath = $this->current();
if (is_dir($filePath) === true) {
if ($this->config->local === true) {
return false;
}
} else if ($this->shouldProcessFile($filePath) === false) {
return false;
}
if ($this->shouldIgnorePath($filePath) === true) {
return false;
}
$this->acceptedPaths[$realPath] = true;
return true;
}//end accept()
/**
* Returns an iterator for the current entry.
*
* Ensures that the ignore patterns are preserved so they don't have
* to be generated each time.
*
* @return \RecursiveIterator
*/
public function getChildren()
{
$children = new static(
new \RecursiveDirectoryIterator($this->current(), (\RecursiveDirectoryIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS)),
$this->basedir,
$this->config,
$this->ruleset
);
// Set the ignore patterns so we don't have to generate them again.
$children->ignoreDirPatterns = $this->ignoreDirPatterns;
$children->ignoreFilePatterns = $this->ignoreFilePatterns;
$children->acceptedPaths = $this->acceptedPaths;
return $children;
}//end getChildren()
/**
* Checks filtering rules to see if a file should be checked.
*
* Checks both file extension filters and path ignore filters.
*
* @param string $path The path to the file being checked.
*
* @return bool
*/
protected function shouldProcessFile($path)
{
// Check that the file's extension is one we are checking.
// We are strict about checking the extension and we don't
// let files through with no extension or that start with a dot.
$fileName = basename($path);
$fileParts = explode('.', $fileName);
if ($fileParts[0] === $fileName || $fileParts[0] === '') {
return false;
}
// Checking multi-part file extensions, so need to create a
// complete extension list and make sure one is allowed.
$extensions = [];
array_shift($fileParts);
foreach ($fileParts as $part) {
$extensions[implode('.', $fileParts)] = 1;
array_shift($fileParts);
}
$matches = array_intersect_key($extensions, $this->config->extensions);
if (empty($matches) === true) {
return false;
}
return true;
}//end shouldProcessFile()
/**
* Checks filtering rules to see if a path should be ignored.
*
* @param string $path The path to the file or directory being checked.
*
* @return bool
*/
protected function shouldIgnorePath($path)
{
if ($this->ignoreFilePatterns === null) {
$this->ignoreDirPatterns = [];
$this->ignoreFilePatterns = [];
$ignorePatterns = array_merge($this->config->ignored, $this->ruleset->getIgnorePatterns());
foreach ($ignorePatterns as $pattern => $type) {
// If the ignore pattern ends with /* then it is ignoring an entire directory.
if (substr($pattern, -2) === '/*') {
// Need to check this pattern for dirs as well as individual file paths.
$pattern = substr($pattern, 0, -2);
$this->ignoreDirPatterns[$pattern] = $type;
$this->ignoreFilePatterns[$pattern] = $type;
} else {
// This is a file-specific pattern, so only need to check this
// for individual file paths.
$this->ignoreFilePatterns[$pattern] = $type;
}
}
}
$relativePath = $path;
if (strpos($path, $this->basedir) === 0) {
// The +1 cuts off the directory separator as well.
$relativePath = substr($path, (strlen($this->basedir) + 1));
}
if (is_dir($path) === true) {
$ignorePatterns = $this->ignoreDirPatterns;
} else {
$ignorePatterns = $this->ignoreFilePatterns;
}
foreach ($ignorePatterns as $pattern => $type) {
// Maintains backwards compatibility in case the ignore pattern does
// not have a relative/absolute value.
if (is_int($pattern) === true) {
$pattern = $type;
$type = 'absolute';
}
$replacements = [
'\\,' => ',',
'*' => '.*',
];
// We assume a / directory separator, as do the exclude rules
// most developers write, so we need a special case for any system
// that is different.
if (DIRECTORY_SEPARATOR === '\\') {
$replacements['/'] = '\\\\';
}
$pattern = strtr($pattern, $replacements);
if ($type === 'relative') {
$testPath = $relativePath;
} else {
$testPath = $path;
}
$pattern = '`'.$pattern.'`i';
if (preg_match($pattern, $testPath) === 1) {
return true;
}
}//end foreach
return false;
}//end shouldIgnorePath()
}//end class

View File

@ -0,0 +1,61 @@
<?php
/**
* A filter to only include files that have been modified or added in a Git repository.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Filters;
use PHP_CodeSniffer\Util;
class GitModified extends ExactMatch
{
/**
* Get a list of blacklisted file paths.
*
* @return array
*/
protected function getBlacklist()
{
return [];
}//end getBlacklist()
/**
* Get a list of whitelisted file paths.
*
* @return array
*/
protected function getWhitelist()
{
$modified = [];
$cmd = 'git ls-files -o -m --exclude-standard -- '.escapeshellarg($this->basedir);
$output = [];
exec($cmd, $output);
$basedir = $this->basedir;
if (is_dir($basedir) === false) {
$basedir = dirname($basedir);
}
foreach ($output as $path) {
$path = Util\Common::realpath($path);
do {
$modified[$path] = true;
$path = dirname($path);
} while ($path !== $basedir);
}
return $modified;
}//end getWhitelist()
}//end class

View File

@ -0,0 +1,723 @@
<?php
/**
* A helper class for fixing errors.
*
* Provides helper functions that act upon a token array and modify the file
* content.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Common;
class Fixer
{
/**
* Is the fixer enabled and fixing a file?
*
* Sniffs should check this value to ensure they are not
* doing extra processing to prepare for a fix when fixing is
* not required.
*
* @var boolean
*/
public $enabled = false;
/**
* The number of times we have looped over a file.
*
* @var integer
*/
public $loops = 0;
/**
* The file being fixed.
*
* @var \PHP_CodeSniffer\Files\File
*/
private $currentFile = null;
/**
* The list of tokens that make up the file contents.
*
* This is a simplified list which just contains the token content and nothing
* else. This is the array that is updated as fixes are made, not the file's
* token array. Imploding this array will give you the file content back.
*
* @var array<int, string>
*/
private $tokens = [];
/**
* A list of tokens that have already been fixed.
*
* We don't allow the same token to be fixed more than once each time
* through a file as this can easily cause conflicts between sniffs.
*
* @var int[]
*/
private $fixedTokens = [];
/**
* The last value of each fixed token.
*
* If a token is being "fixed" back to its last value, the fix is
* probably conflicting with another.
*
* @var array<int, string>
*/
private $oldTokenValues = [];
/**
* A list of tokens that have been fixed during a changeset.
*
* All changes in changeset must be able to be applied, or else
* the entire changeset is rejected.
*
* @var array
*/
private $changeset = [];
/**
* Is there an open changeset.
*
* @var boolean
*/
private $inChangeset = false;
/**
* Is the current fixing loop in conflict?
*
* @var boolean
*/
private $inConflict = false;
/**
* The number of fixes that have been performed.
*
* @var integer
*/
private $numFixes = 0;
/**
* Starts fixing a new file.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being fixed.
*
* @return void
*/
public function startFile(File $phpcsFile)
{
$this->currentFile = $phpcsFile;
$this->numFixes = 0;
$this->fixedTokens = [];
$tokens = $phpcsFile->getTokens();
$this->tokens = [];
foreach ($tokens as $index => $token) {
if (isset($token['orig_content']) === true) {
$this->tokens[$index] = $token['orig_content'];
} else {
$this->tokens[$index] = $token['content'];
}
}
}//end startFile()
/**
* Attempt to fix the file by processing it until no fixes are made.
*
* @return boolean
*/
public function fixFile()
{
$fixable = $this->currentFile->getFixableCount();
if ($fixable === 0) {
// Nothing to fix.
return false;
}
$this->enabled = true;
$this->loops = 0;
while ($this->loops < 50) {
ob_start();
// Only needed once file content has changed.
$contents = $this->getContents();
if (PHP_CODESNIFFER_VERBOSITY > 2) {
@ob_end_clean();
echo '---START FILE CONTENT---'.PHP_EOL;
$lines = explode($this->currentFile->eolChar, $contents);
$max = strlen(count($lines));
foreach ($lines as $lineNum => $line) {
$lineNum++;
echo str_pad($lineNum, $max, ' ', STR_PAD_LEFT).'|'.$line.PHP_EOL;
}
echo '--- END FILE CONTENT ---'.PHP_EOL;
ob_start();
}
$this->inConflict = false;
$this->currentFile->ruleset->populateTokenListeners();
$this->currentFile->setContent($contents);
$this->currentFile->process();
ob_end_clean();
$this->loops++;
if (PHP_CODESNIFFER_CBF === true && PHP_CODESNIFFER_VERBOSITY > 0) {
echo "\r".str_repeat(' ', 80)."\r";
echo "\t=> Fixing file: $this->numFixes/$fixable violations remaining [made $this->loops pass";
if ($this->loops > 1) {
echo 'es';
}
echo ']... ';
}
if ($this->numFixes === 0 && $this->inConflict === false) {
// Nothing left to do.
break;
} else if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "\t* fixed $this->numFixes violations, starting loop ".($this->loops + 1).' *'.PHP_EOL;
}
}//end while
$this->enabled = false;
if ($this->numFixes > 0) {
if (PHP_CODESNIFFER_VERBOSITY > 1) {
if (ob_get_level() > 0) {
ob_end_clean();
}
echo "\t*** Reached maximum number of loops with $this->numFixes violations left unfixed ***".PHP_EOL;
ob_start();
}
return false;
}
return true;
}//end fixFile()
/**
* Generates a text diff of the original file and the new content.
*
* @param string $filePath Optional file path to diff the file against.
* If not specified, the original version of the
* file will be used.
* @param boolean $colors Print colored output or not.
*
* @return string
*/
public function generateDiff($filePath=null, $colors=true)
{
if ($filePath === null) {
$filePath = $this->currentFile->getFilename();
}
$cwd = getcwd().DIRECTORY_SEPARATOR;
if (strpos($filePath, $cwd) === 0) {
$filename = substr($filePath, strlen($cwd));
} else {
$filename = $filePath;
}
$contents = $this->getContents();
$tempName = tempnam(sys_get_temp_dir(), 'phpcs-fixer');
$fixedFile = fopen($tempName, 'w');
fwrite($fixedFile, $contents);
// We must use something like shell_exec() because whitespace at the end
// of lines is critical to diff files.
$filename = escapeshellarg($filename);
$cmd = "diff -u -L$filename -LPHP_CodeSniffer $filename \"$tempName\"";
$diff = shell_exec($cmd);
fclose($fixedFile);
if (is_file($tempName) === true) {
unlink($tempName);
}
if ($colors === false) {
return $diff;
}
$diffLines = explode(PHP_EOL, $diff);
if (count($diffLines) === 1) {
// Seems to be required for cygwin.
$diffLines = explode("\n", $diff);
}
$diff = [];
foreach ($diffLines as $line) {
if (isset($line[0]) === true) {
switch ($line[0]) {
case '-':
$diff[] = "\033[31m$line\033[0m";
break;
case '+':
$diff[] = "\033[32m$line\033[0m";
break;
default:
$diff[] = $line;
}
}
}
$diff = implode(PHP_EOL, $diff);
return $diff;
}//end generateDiff()
/**
* Get a count of fixes that have been performed on the file.
*
* This value is reset every time a new file is started, or an existing
* file is restarted.
*
* @return int
*/
public function getFixCount()
{
return $this->numFixes;
}//end getFixCount()
/**
* Get the current content of the file, as a string.
*
* @return string
*/
public function getContents()
{
$contents = implode($this->tokens);
return $contents;
}//end getContents()
/**
* Get the current fixed content of a token.
*
* This function takes changesets into account so should be used
* instead of directly accessing the token array.
*
* @param int $stackPtr The position of the token in the token stack.
*
* @return string
*/
public function getTokenContent($stackPtr)
{
if ($this->inChangeset === true
&& isset($this->changeset[$stackPtr]) === true
) {
return $this->changeset[$stackPtr];
} else {
return $this->tokens[$stackPtr];
}
}//end getTokenContent()
/**
* Start recording actions for a changeset.
*
* @return void
*/
public function beginChangeset()
{
if ($this->inConflict === true) {
return false;
}
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$bt = debug_backtrace();
$sniff = $bt[1]['class'];
$line = $bt[0]['line'];
@ob_end_clean();
echo "\t=> Changeset started by $sniff (line $line)".PHP_EOL;
ob_start();
}
$this->changeset = [];
$this->inChangeset = true;
}//end beginChangeset()
/**
* Stop recording actions for a changeset, and apply logged changes.
*
* @return boolean
*/
public function endChangeset()
{
if ($this->inConflict === true) {
return false;
}
$this->inChangeset = false;
$success = true;
$applied = [];
foreach ($this->changeset as $stackPtr => $content) {
$success = $this->replaceToken($stackPtr, $content);
if ($success === false) {
break;
} else {
$applied[] = $stackPtr;
}
}
if ($success === false) {
// Rolling back all changes.
foreach ($applied as $stackPtr) {
$this->revertToken($stackPtr);
}
if (PHP_CODESNIFFER_VERBOSITY > 1) {
@ob_end_clean();
echo "\t=> Changeset failed to apply".PHP_EOL;
ob_start();
}
} else if (PHP_CODESNIFFER_VERBOSITY > 1) {
$fixes = count($this->changeset);
@ob_end_clean();
echo "\t=> Changeset ended: $fixes changes applied".PHP_EOL;
ob_start();
}
$this->changeset = [];
}//end endChangeset()
/**
* Stop recording actions for a changeset, and discard logged changes.
*
* @return void
*/
public function rollbackChangeset()
{
$this->inChangeset = false;
$this->inConflict = false;
if (empty($this->changeset) === false) {
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$bt = debug_backtrace();
if ($bt[1]['class'] === 'PHP_CodeSniffer\Fixer') {
$sniff = $bt[2]['class'];
$line = $bt[1]['line'];
} else {
$sniff = $bt[1]['class'];
$line = $bt[0]['line'];
}
$numChanges = count($this->changeset);
@ob_end_clean();
echo "\t\tR: $sniff (line $line) rolled back the changeset ($numChanges changes)".PHP_EOL;
echo "\t=> Changeset rolled back".PHP_EOL;
ob_start();
}
$this->changeset = [];
}//end if
}//end rollbackChangeset()
/**
* Replace the entire contents of a token.
*
* @param int $stackPtr The position of the token in the token stack.
* @param string $content The new content of the token.
*
* @return bool If the change was accepted.
*/
public function replaceToken($stackPtr, $content)
{
if ($this->inConflict === true) {
return false;
}
if ($this->inChangeset === false
&& isset($this->fixedTokens[$stackPtr]) === true
) {
$indent = "\t";
if (empty($this->changeset) === false) {
$indent .= "\t";
}
if (PHP_CODESNIFFER_VERBOSITY > 1) {
@ob_end_clean();
echo "$indent* token $stackPtr has already been modified, skipping *".PHP_EOL;
ob_start();
}
return false;
}
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$bt = debug_backtrace();
if ($bt[1]['class'] === 'PHP_CodeSniffer\Fixer') {
$sniff = $bt[2]['class'];
$line = $bt[1]['line'];
} else {
$sniff = $bt[1]['class'];
$line = $bt[0]['line'];
}
$tokens = $this->currentFile->getTokens();
$type = $tokens[$stackPtr]['type'];
$oldContent = Common::prepareForOutput($this->tokens[$stackPtr]);
$newContent = Common::prepareForOutput($content);
if (trim($this->tokens[$stackPtr]) === '' && isset($this->tokens[($stackPtr + 1)]) === true) {
// Add some context for whitespace only changes.
$append = Common::prepareForOutput($this->tokens[($stackPtr + 1)]);
$oldContent .= $append;
$newContent .= $append;
}
}//end if
if ($this->inChangeset === true) {
$this->changeset[$stackPtr] = $content;
if (PHP_CODESNIFFER_VERBOSITY > 1) {
@ob_end_clean();
echo "\t\tQ: $sniff (line $line) replaced token $stackPtr ($type) \"$oldContent\" => \"$newContent\"".PHP_EOL;
ob_start();
}
return true;
}
if (isset($this->oldTokenValues[$stackPtr]) === false) {
$this->oldTokenValues[$stackPtr] = [
'curr' => $content,
'prev' => $this->tokens[$stackPtr],
'loop' => $this->loops,
];
} else {
if ($this->oldTokenValues[$stackPtr]['prev'] === $content
&& $this->oldTokenValues[$stackPtr]['loop'] === ($this->loops - 1)
) {
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$indent = "\t";
if (empty($this->changeset) === false) {
$indent .= "\t";
}
$loop = $this->oldTokenValues[$stackPtr]['loop'];
@ob_end_clean();
echo "$indent**** $sniff (line $line) has possible conflict with another sniff on loop $loop; caused by the following change ****".PHP_EOL;
echo "$indent**** replaced token $stackPtr ($type) \"$oldContent\" => \"$newContent\" ****".PHP_EOL;
}
if ($this->oldTokenValues[$stackPtr]['loop'] >= ($this->loops - 1)) {
$this->inConflict = true;
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "$indent**** ignoring all changes until next loop ****".PHP_EOL;
}
}
if (PHP_CODESNIFFER_VERBOSITY > 1) {
ob_start();
}
return false;
}//end if
$this->oldTokenValues[$stackPtr]['prev'] = $this->oldTokenValues[$stackPtr]['curr'];
$this->oldTokenValues[$stackPtr]['curr'] = $content;
$this->oldTokenValues[$stackPtr]['loop'] = $this->loops;
}//end if
$this->fixedTokens[$stackPtr] = $this->tokens[$stackPtr];
$this->tokens[$stackPtr] = $content;
$this->numFixes++;
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$indent = "\t";
if (empty($this->changeset) === false) {
$indent .= "\tA: ";
}
if (ob_get_level() > 0) {
ob_end_clean();
}
echo "$indent$sniff (line $line) replaced token $stackPtr ($type) \"$oldContent\" => \"$newContent\"".PHP_EOL;
ob_start();
}
return true;
}//end replaceToken()
/**
* Reverts the previous fix made to a token.
*
* @param int $stackPtr The position of the token in the token stack.
*
* @return bool If a change was reverted.
*/
public function revertToken($stackPtr)
{
if (isset($this->fixedTokens[$stackPtr]) === false) {
return false;
}
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$bt = debug_backtrace();
if ($bt[1]['class'] === 'PHP_CodeSniffer\Fixer') {
$sniff = $bt[2]['class'];
$line = $bt[1]['line'];
} else {
$sniff = $bt[1]['class'];
$line = $bt[0]['line'];
}
$tokens = $this->currentFile->getTokens();
$type = $tokens[$stackPtr]['type'];
$oldContent = Common::prepareForOutput($this->tokens[$stackPtr]);
$newContent = Common::prepareForOutput($this->fixedTokens[$stackPtr]);
if (trim($this->tokens[$stackPtr]) === '' && isset($tokens[($stackPtr + 1)]) === true) {
// Add some context for whitespace only changes.
$append = Common::prepareForOutput($this->tokens[($stackPtr + 1)]);
$oldContent .= $append;
$newContent .= $append;
}
}//end if
$this->tokens[$stackPtr] = $this->fixedTokens[$stackPtr];
unset($this->fixedTokens[$stackPtr]);
$this->numFixes--;
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$indent = "\t";
if (empty($this->changeset) === false) {
$indent .= "\tR: ";
}
@ob_end_clean();
echo "$indent$sniff (line $line) reverted token $stackPtr ($type) \"$oldContent\" => \"$newContent\"".PHP_EOL;
ob_start();
}
return true;
}//end revertToken()
/**
* Replace the content of a token with a part of its current content.
*
* @param int $stackPtr The position of the token in the token stack.
* @param int $start The first character to keep.
* @param int $length The number of chacters to keep. If NULL, the content of
* the token from $start to the end of the content is kept.
*
* @return bool If the change was accepted.
*/
public function substrToken($stackPtr, $start, $length=null)
{
$current = $this->getTokenContent($stackPtr);
if ($length === null) {
$newContent = substr($current, $start);
} else {
$newContent = substr($current, $start, $length);
}
return $this->replaceToken($stackPtr, $newContent);
}//end substrToken()
/**
* Adds a newline to end of a token's content.
*
* @param int $stackPtr The position of the token in the token stack.
*
* @return bool If the change was accepted.
*/
public function addNewline($stackPtr)
{
$current = $this->getTokenContent($stackPtr);
return $this->replaceToken($stackPtr, $current.$this->currentFile->eolChar);
}//end addNewline()
/**
* Adds a newline to the start of a token's content.
*
* @param int $stackPtr The position of the token in the token stack.
*
* @return bool If the change was accepted.
*/
public function addNewlineBefore($stackPtr)
{
$current = $this->getTokenContent($stackPtr);
return $this->replaceToken($stackPtr, $this->currentFile->eolChar.$current);
}//end addNewlineBefore()
/**
* Adds content to the end of a token's current content.
*
* @param int $stackPtr The position of the token in the token stack.
* @param string $content The content to add.
*
* @return bool If the change was accepted.
*/
public function addContent($stackPtr, $content)
{
$current = $this->getTokenContent($stackPtr);
return $this->replaceToken($stackPtr, $current.$content);
}//end addContent()
/**
* Adds content to the start of a token's current content.
*
* @param int $stackPtr The position of the token in the token stack.
* @param string $content The content to add.
*
* @return bool If the change was accepted.
*/
public function addContentBefore($stackPtr, $content)
{
$current = $this->getTokenContent($stackPtr);
return $this->replaceToken($stackPtr, $content.$current);
}//end addContentBefore()
}//end class

View File

@ -0,0 +1,118 @@
<?php
/**
* The base class for all PHP_CodeSniffer documentation generators.
*
* Documentation generators are used to print documentation about code sniffs
* in a standard.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Generators;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Autoload;
use PHP_CodeSniffer\Util\Common;
abstract class Generator
{
/**
* The ruleset used for the run.
*
* @var \PHP_CodeSniffer\Ruleset
*/
public $ruleset = null;
/**
* XML documentation files used to produce the final output.
*
* @var string[]
*/
public $docFiles = [];
/**
* Constructs a doc generator.
*
* @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
*
* @see generate()
*/
public function __construct(Ruleset $ruleset)
{
$this->ruleset = $ruleset;
foreach ($ruleset->sniffs as $className => $sniffClass) {
$file = Autoload::getLoadedFileName($className);
$docFile = str_replace(
DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR,
DIRECTORY_SEPARATOR.'Docs'.DIRECTORY_SEPARATOR,
$file
);
$docFile = str_replace('Sniff.php', 'Standard.xml', $docFile);
if (is_file($docFile) === true) {
$this->docFiles[] = $docFile;
}
}
}//end __construct()
/**
* Retrieves the title of the sniff from the DOMNode supplied.
*
* @param \DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return string
*/
protected function getTitle(\DOMNode $doc)
{
return $doc->getAttribute('title');
}//end getTitle()
/**
* Generates the documentation for a standard.
*
* It's probably wise for doc generators to override this method so they
* have control over how the docs are produced. Otherwise, the processSniff
* method should be overridden to output content for each sniff.
*
* @return void
* @see processSniff()
*/
public function generate()
{
foreach ($this->docFiles as $file) {
$doc = new \DOMDocument();
$doc->load($file);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$this->processSniff($documentation);
}
}//end generate()
/**
* Process the documentation for a single sniff.
*
* Doc generators must implement this function to produce output.
*
* @param \DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return void
* @see generate()
*/
abstract protected function processSniff(\DOMNode $doc);
}//end class

View File

@ -0,0 +1,270 @@
<?php
/**
* A doc generator that outputs documentation in one big HTML file.
*
* Output is in one large HTML file and is designed for you to style with
* your own stylesheet. It contains a table of contents at the top with anchors
* to each sniff.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Generators;
use PHP_CodeSniffer\Config;
class HTML extends Generator
{
/**
* Generates the documentation for a standard.
*
* @return void
* @see processSniff()
*/
public function generate()
{
ob_start();
$this->printHeader();
$this->printToc();
foreach ($this->docFiles as $file) {
$doc = new \DOMDocument();
$doc->load($file);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$this->processSniff($documentation);
}
$this->printFooter();
$content = ob_get_contents();
ob_end_clean();
echo $content;
}//end generate()
/**
* Print the header of the HTML page.
*
* @return void
*/
protected function printHeader()
{
$standard = $this->ruleset->name;
echo '<html>'.PHP_EOL;
echo ' <head>'.PHP_EOL;
echo " <title>$standard Coding Standards</title>".PHP_EOL;
echo ' <style>
body {
background-color: #FFFFFF;
font-size: 14px;
font-family: Arial, Helvetica, sans-serif;
color: #000000;
}
h1 {
color: #666666;
font-size: 20px;
font-weight: bold;
margin-top: 0px;
background-color: #E6E7E8;
padding: 20px;
border: 1px solid #BBBBBB;
}
h2 {
color: #00A5E3;
font-size: 16px;
font-weight: normal;
margin-top: 50px;
}
.code-comparison {
width: 100%;
}
.code-comparison td {
border: 1px solid #CCCCCC;
}
.code-comparison-title, .code-comparison-code {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
color: #000000;
vertical-align: top;
padding: 4px;
width: 50%;
background-color: #F1F1F1;
line-height: 15px;
}
.code-comparison-code {
font-family: Courier;
background-color: #F9F9F9;
}
.code-comparison-highlight {
background-color: #DDF1F7;
border: 1px solid #00A5E3;
line-height: 15px;
}
.tag-line {
text-align: center;
width: 100%;
margin-top: 30px;
font-size: 12px;
}
.tag-line a {
color: #000000;
}
</style>'.PHP_EOL;
echo ' </head>'.PHP_EOL;
echo ' <body>'.PHP_EOL;
echo " <h1>$standard Coding Standards</h1>".PHP_EOL;
}//end printHeader()
/**
* Print the table of contents for the standard.
*
* The TOC is just an unordered list of bookmarks to sniffs on the page.
*
* @return void
*/
protected function printToc()
{
echo ' <h2>Table of Contents</h2>'.PHP_EOL;
echo ' <ul class="toc">'.PHP_EOL;
foreach ($this->docFiles as $file) {
$doc = new \DOMDocument();
$doc->load($file);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$title = $this->getTitle($documentation);
echo ' <li><a href="#'.str_replace(' ', '-', $title)."\">$title</a></li>".PHP_EOL;
}
echo ' </ul>'.PHP_EOL;
}//end printToc()
/**
* Print the footer of the HTML page.
*
* @return void
*/
protected function printFooter()
{
// Turn off errors so we don't get timezone warnings if people
// don't have their timezone set.
$errorLevel = error_reporting(0);
echo ' <div class="tag-line">';
echo 'Documentation generated on '.date('r');
echo ' by <a href="https://github.com/squizlabs/PHP_CodeSniffer">PHP_CodeSniffer '.Config::VERSION.'</a>';
echo '</div>'.PHP_EOL;
error_reporting($errorLevel);
echo ' </body>'.PHP_EOL;
echo '</html>'.PHP_EOL;
}//end printFooter()
/**
* Process the documentation for a single sniff.
*
* @param \DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return void
*/
public function processSniff(\DOMNode $doc)
{
$title = $this->getTitle($doc);
echo ' <a name="'.str_replace(' ', '-', $title).'" />'.PHP_EOL;
echo " <h2>$title</h2>".PHP_EOL;
foreach ($doc->childNodes as $node) {
if ($node->nodeName === 'standard') {
$this->printTextBlock($node);
} else if ($node->nodeName === 'code_comparison') {
$this->printCodeComparisonBlock($node);
}
}
}//end processSniff()
/**
* Print a text block found in a standard.
*
* @param \DOMNode $node The DOMNode object for the text block.
*
* @return void
*/
protected function printTextBlock(\DOMNode $node)
{
$content = trim($node->nodeValue);
$content = htmlspecialchars($content);
// Allow em tags only.
$content = str_replace('&lt;em&gt;', '<em>', $content);
$content = str_replace('&lt;/em&gt;', '</em>', $content);
echo " <p class=\"text\">$content</p>".PHP_EOL;
}//end printTextBlock()
/**
* Print a code comparison block found in a standard.
*
* @param \DOMNode $node The DOMNode object for the code comparison block.
*
* @return void
*/
protected function printCodeComparisonBlock(\DOMNode $node)
{
$codeBlocks = $node->getElementsByTagName('code');
$firstTitle = $codeBlocks->item(0)->getAttribute('title');
$first = trim($codeBlocks->item(0)->nodeValue);
$first = str_replace('<?php', '&lt;?php', $first);
$first = str_replace("\n", '</br>', $first);
$first = str_replace(' ', '&nbsp;', $first);
$first = str_replace('<em>', '<span class="code-comparison-highlight">', $first);
$first = str_replace('</em>', '</span>', $first);
$secondTitle = $codeBlocks->item(1)->getAttribute('title');
$second = trim($codeBlocks->item(1)->nodeValue);
$second = str_replace('<?php', '&lt;?php', $second);
$second = str_replace("\n", '</br>', $second);
$second = str_replace(' ', '&nbsp;', $second);
$second = str_replace('<em>', '<span class="code-comparison-highlight">', $second);
$second = str_replace('</em>', '</span>', $second);
echo ' <table class="code-comparison">'.PHP_EOL;
echo ' <tr>'.PHP_EOL;
echo " <td class=\"code-comparison-title\">$firstTitle</td>".PHP_EOL;
echo " <td class=\"code-comparison-title\">$secondTitle</td>".PHP_EOL;
echo ' </tr>'.PHP_EOL;
echo ' <tr>'.PHP_EOL;
echo " <td class=\"code-comparison-code\">$first</td>".PHP_EOL;
echo " <td class=\"code-comparison-code\">$second</td>".PHP_EOL;
echo ' </tr>'.PHP_EOL;
echo ' </table>'.PHP_EOL;
}//end printCodeComparisonBlock()
}//end class

View File

@ -0,0 +1,161 @@
<?php
/**
* A doc generator that outputs documentation in Markdown format.
*
* @author Stefano Kowalke <blueduck@gmx.net>
* @copyright 2014 Arroba IT
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Generators;
use PHP_CodeSniffer\Config;
class Markdown extends Generator
{
/**
* Generates the documentation for a standard.
*
* @return void
* @see processSniff()
*/
public function generate()
{
ob_start();
$this->printHeader();
foreach ($this->docFiles as $file) {
$doc = new \DOMDocument();
$doc->load($file);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$this->processSniff($documentation);
}
$this->printFooter();
$content = ob_get_contents();
ob_end_clean();
echo $content;
}//end generate()
/**
* Print the markdown header.
*
* @return void
*/
protected function printHeader()
{
$standard = $this->ruleset->name;
echo "# $standard Coding Standard".PHP_EOL;
}//end printHeader()
/**
* Print the markdown footer.
*
* @return void
*/
protected function printFooter()
{
// Turn off errors so we don't get timezone warnings if people
// don't have their timezone set.
error_reporting(0);
echo 'Documentation generated on '.date('r');
echo ' by [PHP_CodeSniffer '.Config::VERSION.'](https://github.com/squizlabs/PHP_CodeSniffer)'.PHP_EOL;
}//end printFooter()
/**
* Process the documentation for a single sniff.
*
* @param \DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return void
*/
protected function processSniff(\DOMNode $doc)
{
$title = $this->getTitle($doc);
echo "## $title".PHP_EOL;
foreach ($doc->childNodes as $node) {
if ($node->nodeName === 'standard') {
$this->printTextBlock($node);
} else if ($node->nodeName === 'code_comparison') {
$this->printCodeComparisonBlock($node);
}
}
}//end processSniff()
/**
* Print a text block found in a standard.
*
* @param \DOMNode $node The DOMNode object for the text block.
*
* @return void
*/
protected function printTextBlock(\DOMNode $node)
{
$content = trim($node->nodeValue);
$content = htmlspecialchars($content);
$content = str_replace('&lt;em&gt;', '*', $content);
$content = str_replace('&lt;/em&gt;', '*', $content);
echo $content.PHP_EOL;
}//end printTextBlock()
/**
* Print a code comparison block found in a standard.
*
* @param \DOMNode $node The DOMNode object for the code comparison block.
*
* @return void
*/
protected function printCodeComparisonBlock(\DOMNode $node)
{
$codeBlocks = $node->getElementsByTagName('code');
$firstTitle = $codeBlocks->item(0)->getAttribute('title');
$first = trim($codeBlocks->item(0)->nodeValue);
$first = str_replace("\n", "\n ", $first);
$first = str_replace('<em>', '', $first);
$first = str_replace('</em>', '', $first);
$secondTitle = $codeBlocks->item(1)->getAttribute('title');
$second = trim($codeBlocks->item(1)->nodeValue);
$second = str_replace("\n", "\n ", $second);
$second = str_replace('<em>', '', $second);
$second = str_replace('</em>', '', $second);
echo ' <table>'.PHP_EOL;
echo ' <tr>'.PHP_EOL;
echo " <th>$firstTitle</th>".PHP_EOL;
echo " <th>$secondTitle</th>".PHP_EOL;
echo ' </tr>'.PHP_EOL;
echo ' <tr>'.PHP_EOL;
echo '<td>'.PHP_EOL.PHP_EOL;
echo " $first".PHP_EOL.PHP_EOL;
echo '</td>'.PHP_EOL;
echo '<td>'.PHP_EOL.PHP_EOL;
echo " $second".PHP_EOL.PHP_EOL;
echo '</td>'.PHP_EOL;
echo ' </tr>'.PHP_EOL;
echo ' </table>'.PHP_EOL;
}//end printCodeComparisonBlock()
}//end class

View File

@ -0,0 +1,245 @@
<?php
/**
* A doc generator that outputs text-based documentation.
*
* Output is designed to be displayed in a terminal and is wrapped to 100 characters.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Generators;
class Text extends Generator
{
/**
* Process the documentation for a single sniff.
*
* @param \DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return void
*/
public function processSniff(\DOMNode $doc)
{
$this->printTitle($doc);
foreach ($doc->childNodes as $node) {
if ($node->nodeName === 'standard') {
$this->printTextBlock($node);
} else if ($node->nodeName === 'code_comparison') {
$this->printCodeComparisonBlock($node);
}
}
}//end processSniff()
/**
* Prints the title area for a single sniff.
*
* @param \DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return void
*/
protected function printTitle(\DOMNode $doc)
{
$title = $this->getTitle($doc);
$standard = $this->ruleset->name;
echo PHP_EOL;
echo str_repeat('-', (strlen("$standard CODING STANDARD: $title") + 4));
echo strtoupper(PHP_EOL."| $standard CODING STANDARD: $title |".PHP_EOL);
echo str_repeat('-', (strlen("$standard CODING STANDARD: $title") + 4));
echo PHP_EOL.PHP_EOL;
}//end printTitle()
/**
* Print a text block found in a standard.
*
* @param \DOMNode $node The DOMNode object for the text block.
*
* @return void
*/
protected function printTextBlock(\DOMNode $node)
{
$text = trim($node->nodeValue);
$text = str_replace('<em>', '*', $text);
$text = str_replace('</em>', '*', $text);
$lines = [];
$tempLine = '';
$words = explode(' ', $text);
foreach ($words as $word) {
if (strlen($tempLine.$word) >= 99) {
if (strlen($tempLine.$word) === 99) {
// Adding the extra space will push us to the edge
// so we are done.
$lines[] = $tempLine.$word;
$tempLine = '';
} else if (strlen($tempLine.$word) === 100) {
// We are already at the edge, so we are done.
$lines[] = $tempLine.$word;
$tempLine = '';
} else {
$lines[] = rtrim($tempLine);
$tempLine = $word.' ';
}
} else {
$tempLine .= $word.' ';
}
}//end foreach
if ($tempLine !== '') {
$lines[] = rtrim($tempLine);
}
echo implode(PHP_EOL, $lines).PHP_EOL.PHP_EOL;
}//end printTextBlock()
/**
* Print a code comparison block found in a standard.
*
* @param \DOMNode $node The DOMNode object for the code comparison block.
*
* @return void
*/
protected function printCodeComparisonBlock(\DOMNode $node)
{
$codeBlocks = $node->getElementsByTagName('code');
$first = trim($codeBlocks->item(0)->nodeValue);
$firstTitle = $codeBlocks->item(0)->getAttribute('title');
$firstTitleLines = [];
$tempTitle = '';
$words = explode(' ', $firstTitle);
foreach ($words as $word) {
if (strlen($tempTitle.$word) >= 45) {
if (strlen($tempTitle.$word) === 45) {
// Adding the extra space will push us to the edge
// so we are done.
$firstTitleLines[] = $tempTitle.$word;
$tempTitle = '';
} else if (strlen($tempTitle.$word) === 46) {
// We are already at the edge, so we are done.
$firstTitleLines[] = $tempTitle.$word;
$tempTitle = '';
} else {
$firstTitleLines[] = $tempTitle;
$tempTitle = $word;
}
} else {
$tempTitle .= $word.' ';
}
}//end foreach
if ($tempTitle !== '') {
$firstTitleLines[] = $tempTitle;
}
$first = str_replace('<em>', '', $first);
$first = str_replace('</em>', '', $first);
$firstLines = explode("\n", $first);
$second = trim($codeBlocks->item(1)->nodeValue);
$secondTitle = $codeBlocks->item(1)->getAttribute('title');
$secondTitleLines = [];
$tempTitle = '';
$words = explode(' ', $secondTitle);
foreach ($words as $word) {
if (strlen($tempTitle.$word) >= 45) {
if (strlen($tempTitle.$word) === 45) {
// Adding the extra space will push us to the edge
// so we are done.
$secondTitleLines[] = $tempTitle.$word;
$tempTitle = '';
} else if (strlen($tempTitle.$word) === 46) {
// We are already at the edge, so we are done.
$secondTitleLines[] = $tempTitle.$word;
$tempTitle = '';
} else {
$secondTitleLines[] = $tempTitle;
$tempTitle = $word;
}
} else {
$tempTitle .= $word.' ';
}
}//end foreach
if ($tempTitle !== '') {
$secondTitleLines[] = $tempTitle;
}
$second = str_replace('<em>', '', $second);
$second = str_replace('</em>', '', $second);
$secondLines = explode("\n", $second);
$maxCodeLines = max(count($firstLines), count($secondLines));
$maxTitleLines = max(count($firstTitleLines), count($secondTitleLines));
echo str_repeat('-', 41);
echo ' CODE COMPARISON ';
echo str_repeat('-', 42).PHP_EOL;
for ($i = 0; $i < $maxTitleLines; $i++) {
if (isset($firstTitleLines[$i]) === true) {
$firstLineText = $firstTitleLines[$i];
} else {
$firstLineText = '';
}
if (isset($secondTitleLines[$i]) === true) {
$secondLineText = $secondTitleLines[$i];
} else {
$secondLineText = '';
}
echo '| ';
echo $firstLineText.str_repeat(' ', (46 - strlen($firstLineText)));
echo ' | ';
echo $secondLineText.str_repeat(' ', (47 - strlen($secondLineText)));
echo ' |'.PHP_EOL;
}//end for
echo str_repeat('-', 100).PHP_EOL;
for ($i = 0; $i < $maxCodeLines; $i++) {
if (isset($firstLines[$i]) === true) {
$firstLineText = $firstLines[$i];
} else {
$firstLineText = '';
}
if (isset($secondLines[$i]) === true) {
$secondLineText = $secondLines[$i];
} else {
$secondLineText = '';
}
echo '| ';
echo $firstLineText.str_repeat(' ', (47 - strlen($firstLineText)));
echo '| ';
echo $secondLineText.str_repeat(' ', (48 - strlen($secondLineText)));
echo '|'.PHP_EOL;
}//end for
echo str_repeat('-', 100).PHP_EOL.PHP_EOL;
}//end printCodeComparisonBlock()
}//end class

View File

@ -0,0 +1,400 @@
<?php
/**
* Manages reporting of errors and warnings.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer;
use PHP_CodeSniffer\Reports\Report;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Exceptions\RuntimeException;
use PHP_CodeSniffer\Exceptions\DeepExitException;
use PHP_CodeSniffer\Util\Common;
class Reporter
{
/**
* The config data for the run.
*
* @var \PHP_CodeSniffer\Config
*/
public $config = null;
/**
* Total number of files that contain errors or warnings.
*
* @var integer
*/
public $totalFiles = 0;
/**
* Total number of errors found during the run.
*
* @var integer
*/
public $totalErrors = 0;
/**
* Total number of warnings found during the run.
*
* @var integer
*/
public $totalWarnings = 0;
/**
* Total number of errors/warnings that can be fixed.
*
* @var integer
*/
public $totalFixable = 0;
/**
* Total number of errors/warnings that were fixed.
*
* @var integer
*/
public $totalFixed = 0;
/**
* When the PHPCS run started.
*
* @var float
*/
public static $startTime = 0;
/**
* A cache of report objects.
*
* @var array
*/
private $reports = [];
/**
* A cache of opened temporary files.
*
* @var array
*/
private $tmpFiles = [];
/**
* Initialise the reporter.
*
* All reports specified in the config will be created and their
* output file (or a temp file if none is specified) initialised by
* clearing the current contents.
*
* @param \PHP_CodeSniffer\Config $config The config data for the run.
*
* @return void
* @throws RuntimeException If a report is not available.
*/
public function __construct(Config $config)
{
$this->config = $config;
foreach ($config->reports as $type => $output) {
$type = ucfirst($type);
if ($output === null) {
$output = $config->reportFile;
}
if (strpos($type, '.') !== false) {
// This is a path to a custom report class.
$filename = realpath($type);
if ($filename === false) {
$error = "ERROR: Custom report \"$type\" not found".PHP_EOL;
throw new DeepExitException($error, 3);
}
$reportClassName = Autoload::loadFile($filename);
} else {
$reportClassName = 'PHP_CodeSniffer\Reports\\'.$type;
}
$reportClass = new $reportClassName();
if (false === ($reportClass instanceof Report)) {
throw new RuntimeException('Class "'.$reportClassName.'" must implement the "PHP_CodeSniffer\Report" interface.');
}
$this->reports[$type] = [
'output' => $output,
'class' => $reportClass,
];
if ($output === null) {
// Using a temp file.
// This needs to be set in the constructor so that all
// child procs use the same report file when running in parallel.
$this->tmpFiles[$type] = tempnam(sys_get_temp_dir(), 'phpcs');
file_put_contents($this->tmpFiles[$type], '');
} else {
file_put_contents($output, '');
}
}//end foreach
}//end __construct()
/**
* Generates and prints final versions of all reports.
*
* Returns TRUE if any of the reports output content to the screen
* or FALSE if all reports were silently printed to a file.
*
* @return bool
*/
public function printReports()
{
$toScreen = false;
foreach ($this->reports as $type => $report) {
if ($report['output'] === null) {
$toScreen = true;
}
$this->printReport($type);
}
return $toScreen;
}//end printReports()
/**
* Generates and prints a single final report.
*
* @param string $report The report type to print.
*
* @return void
*/
public function printReport($report)
{
$report = ucfirst($report);
$reportClass = $this->reports[$report]['class'];
$reportFile = $this->reports[$report]['output'];
if ($reportFile !== null) {
$filename = $reportFile;
$toScreen = false;
} else {
if (isset($this->tmpFiles[$report]) === true) {
$filename = $this->tmpFiles[$report];
} else {
$filename = null;
}
$toScreen = true;
}
$reportCache = '';
if ($filename !== null) {
$reportCache = file_get_contents($filename);
}
ob_start();
$reportClass->generate(
$reportCache,
$this->totalFiles,
$this->totalErrors,
$this->totalWarnings,
$this->totalFixable,
$this->config->showSources,
$this->config->reportWidth,
$this->config->interactive,
$toScreen
);
$generatedReport = ob_get_contents();
ob_end_clean();
if ($this->config->colors !== true || $reportFile !== null) {
$generatedReport = preg_replace('`\033\[[0-9;]+m`', '', $generatedReport);
}
if ($reportFile !== null) {
if (PHP_CODESNIFFER_VERBOSITY > 0) {
echo $generatedReport;
}
file_put_contents($reportFile, $generatedReport.PHP_EOL);
} else {
echo $generatedReport;
if ($filename !== null && file_exists($filename) === true) {
unlink($filename);
unset($this->tmpFiles[$report]);
}
}
}//end printReport()
/**
* Caches the result of a single processed file for all reports.
*
* The report content that is generated is appended to the output file
* assigned to each report. This content may be an intermediate report format
* and not reflect the final report output.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file that has been processed.
*
* @return void
*/
public function cacheFileReport(File $phpcsFile)
{
if (isset($this->config->reports) === false) {
// This happens during unit testing, or any time someone just wants
// the error data and not the printed report.
return;
}
$reportData = $this->prepareFileReport($phpcsFile);
$errorsShown = false;
foreach ($this->reports as $type => $report) {
$reportClass = $report['class'];
ob_start();
$result = $reportClass->generateFileReport($reportData, $phpcsFile, $this->config->showSources, $this->config->reportWidth);
if ($result === true) {
$errorsShown = true;
}
$generatedReport = ob_get_contents();
ob_end_clean();
if ($report['output'] === null) {
// Using a temp file.
if (isset($this->tmpFiles[$type]) === false) {
// When running in interactive mode, the reporter prints the full
// report many times, which will unlink the temp file. So we need
// to create a new one if it doesn't exist.
$this->tmpFiles[$type] = tempnam(sys_get_temp_dir(), 'phpcs');
file_put_contents($this->tmpFiles[$type], '');
}
file_put_contents($this->tmpFiles[$type], $generatedReport, (FILE_APPEND | LOCK_EX));
} else {
file_put_contents($report['output'], $generatedReport, (FILE_APPEND | LOCK_EX));
}//end if
}//end foreach
if ($errorsShown === true || PHP_CODESNIFFER_CBF === true) {
$this->totalFiles++;
$this->totalErrors += $reportData['errors'];
$this->totalWarnings += $reportData['warnings'];
// When PHPCBF is running, we need to use the fixable error values
// after the report has run and fixed what it can.
if (PHP_CODESNIFFER_CBF === true) {
$this->totalFixable += $phpcsFile->getFixableCount();
$this->totalFixed += $phpcsFile->getFixedCount();
} else {
$this->totalFixable += $reportData['fixable'];
}
}
}//end cacheFileReport()
/**
* Generate summary information to be used during report generation.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file that has been processed.
*
* @return array
*/
public function prepareFileReport(File $phpcsFile)
{
$report = [
'filename' => Common::stripBasepath($phpcsFile->getFilename(), $this->config->basepath),
'errors' => $phpcsFile->getErrorCount(),
'warnings' => $phpcsFile->getWarningCount(),
'fixable' => $phpcsFile->getFixableCount(),
'messages' => [],
];
if ($report['errors'] === 0 && $report['warnings'] === 0) {
// Prefect score!
return $report;
}
if ($this->config->recordErrors === false) {
$message = 'Errors are not being recorded but this report requires error messages. ';
$message .= 'This report will not show the correct information.';
$report['messages'][1][1] = [
[
'message' => $message,
'source' => 'Internal.RecordErrors',
'severity' => 5,
'fixable' => false,
'type' => 'ERROR',
],
];
return $report;
}
$errors = [];
// Merge errors and warnings.
foreach ($phpcsFile->getErrors() as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
$newErrors = [];
foreach ($colErrors as $data) {
$newErrors[] = [
'message' => $data['message'],
'source' => $data['source'],
'severity' => $data['severity'],
'fixable' => $data['fixable'],
'type' => 'ERROR',
];
}
$errors[$line][$column] = $newErrors;
}
ksort($errors[$line]);
}//end foreach
foreach ($phpcsFile->getWarnings() as $line => $lineWarnings) {
foreach ($lineWarnings as $column => $colWarnings) {
$newWarnings = [];
foreach ($colWarnings as $data) {
$newWarnings[] = [
'message' => $data['message'],
'source' => $data['source'],
'severity' => $data['severity'],
'fixable' => $data['fixable'],
'type' => 'WARNING',
];
}
if (isset($errors[$line]) === false) {
$errors[$line] = [];
}
if (isset($errors[$line][$column]) === true) {
$errors[$line][$column] = array_merge(
$newWarnings,
$errors[$line][$column]
);
} else {
$errors[$line][$column] = $newWarnings;
}
}//end foreach
ksort($errors[$line]);
}//end foreach
ksort($errors);
$report['messages'] = $errors;
return $report;
}//end prepareFileReport()
}//end class

View File

@ -0,0 +1,249 @@
<?php
/**
* CBF report for PHP_CodeSniffer.
*
* This report implements the various auto-fixing features of the
* PHPCBF script and is not intended (or allowed) to be selected as a
* report from the command line.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Exceptions\DeepExitException;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util;
class Cbf implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
$errors = $phpcsFile->getFixableCount();
if ($errors !== 0) {
if (PHP_CODESNIFFER_VERBOSITY > 0) {
ob_end_clean();
$startTime = microtime(true);
echo "\t=> Fixing file: $errors/$errors violations remaining";
}
$fixed = $phpcsFile->fixer->fixFile();
}
if ($phpcsFile->config->stdin === true) {
// Replacing STDIN, so output current file to STDOUT
// even if nothing was fixed. Exit here because we
// can't process any more than 1 file in this setup.
$fixedContent = $phpcsFile->fixer->getContents();
throw new DeepExitException($fixedContent, 1);
}
if ($errors === 0) {
return false;
}
if (PHP_CODESNIFFER_VERBOSITY > 0) {
if ($fixed === false) {
echo 'ERROR';
} else {
echo 'DONE';
}
$timeTaken = ((microtime(true) - $startTime) * 1000);
if ($timeTaken < 1000) {
$timeTaken = round($timeTaken);
echo " in {$timeTaken}ms".PHP_EOL;
} else {
$timeTaken = round(($timeTaken / 1000), 2);
echo " in $timeTaken secs".PHP_EOL;
}
}
if ($fixed === true) {
// The filename in the report may be truncated due to a basepath setting
// but we are using it for writing here and not display,
// so find the correct path if basepath is in use.
$newFilename = $report['filename'].$phpcsFile->config->suffix;
if ($phpcsFile->config->basepath !== null) {
$newFilename = $phpcsFile->config->basepath.DIRECTORY_SEPARATOR.$newFilename;
}
$newContent = $phpcsFile->fixer->getContents();
file_put_contents($newFilename, $newContent);
if (PHP_CODESNIFFER_VERBOSITY > 0) {
if ($newFilename === $report['filename']) {
echo "\t=> File was overwritten".PHP_EOL;
} else {
echo "\t=> Fixed file written to ".basename($newFilename).PHP_EOL;
}
}
}
if (PHP_CODESNIFFER_VERBOSITY > 0) {
ob_start();
}
$errorCount = $phpcsFile->getErrorCount();
$warningCount = $phpcsFile->getWarningCount();
$fixableCount = $phpcsFile->getFixableCount();
$fixedCount = ($errors - $fixableCount);
echo $report['filename'].">>$errorCount>>$warningCount>>$fixableCount>>$fixedCount".PHP_EOL;
return $fixed;
}//end generateFileReport()
/**
* Prints a summary of fixed files.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
$lines = explode(PHP_EOL, $cachedData);
array_pop($lines);
if (empty($lines) === true) {
echo PHP_EOL.'No fixable errors were found'.PHP_EOL;
return;
}
$reportFiles = [];
$maxLength = 0;
$totalFixed = 0;
$failures = 0;
foreach ($lines as $line) {
$parts = explode('>>', $line);
$fileLen = strlen($parts[0]);
$reportFiles[$parts[0]] = [
'errors' => $parts[1],
'warnings' => $parts[2],
'fixable' => $parts[3],
'fixed' => $parts[4],
'strlen' => $fileLen,
];
$maxLength = max($maxLength, $fileLen);
$totalFixed += $parts[4];
if ($parts[3] > 0) {
$failures++;
}
}
$width = min($width, ($maxLength + 21));
$width = max($width, 70);
echo PHP_EOL."\033[1m".'PHPCBF RESULT SUMMARY'."\033[0m".PHP_EOL;
echo str_repeat('-', $width).PHP_EOL;
echo "\033[1m".'FILE'.str_repeat(' ', ($width - 20)).'FIXED REMAINING'."\033[0m".PHP_EOL;
echo str_repeat('-', $width).PHP_EOL;
foreach ($reportFiles as $file => $data) {
$padding = ($width - 18 - $data['strlen']);
if ($padding < 0) {
$file = '...'.substr($file, (($padding * -1) + 3));
$padding = 0;
}
echo $file.str_repeat(' ', $padding).' ';
if ($data['fixable'] > 0) {
echo "\033[31mFAILED TO FIX\033[0m".PHP_EOL;
continue;
}
$remaining = ($data['errors'] + $data['warnings']);
if ($data['fixed'] !== 0) {
echo $data['fixed'];
echo str_repeat(' ', (7 - strlen((string) $data['fixed'])));
} else {
echo '0 ';
}
if ($remaining !== 0) {
echo $remaining;
} else {
echo '0';
}
echo PHP_EOL;
}//end foreach
echo str_repeat('-', $width).PHP_EOL;
echo "\033[1mA TOTAL OF $totalFixed ERROR";
if ($totalFixed !== 1) {
echo 'S';
}
$numFiles = count($reportFiles);
echo ' WERE FIXED IN '.$numFiles.' FILE';
if ($numFiles !== 1) {
echo 'S';
}
echo "\033[0m";
if ($failures > 0) {
echo PHP_EOL.str_repeat('-', $width).PHP_EOL;
echo "\033[1mPHPCBF FAILED TO FIX $failures FILE";
if ($failures !== 1) {
echo 'S';
}
echo "\033[0m";
}
echo PHP_EOL.str_repeat('-', $width).PHP_EOL.PHP_EOL;
if ($toScreen === true && $interactive === false) {
Util\Timing::printRunTime();
}
}//end generate()
}//end class

View File

@ -0,0 +1,109 @@
<?php
/**
* Checkstyle report for PHP_CodeSniffer.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\File;
class Checkstyle implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
$out = new \XMLWriter;
$out->openMemory();
$out->setIndent(true);
if ($report['errors'] === 0 && $report['warnings'] === 0) {
// Nothing to print.
return false;
}
$out->startElement('file');
$out->writeAttribute('name', $report['filename']);
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$error['type'] = strtolower($error['type']);
if ($phpcsFile->config->encoding !== 'utf-8') {
$error['message'] = iconv($phpcsFile->config->encoding, 'utf-8', $error['message']);
}
$out->startElement('error');
$out->writeAttribute('line', $line);
$out->writeAttribute('column', $column);
$out->writeAttribute('severity', $error['type']);
$out->writeAttribute('message', $error['message']);
$out->writeAttribute('source', $error['source']);
$out->endElement();
}
}
}//end foreach
$out->endElement();
echo $out->flush();
return true;
}//end generateFileReport()
/**
* Prints all violations for processed files, in a Checkstyle format.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
echo '<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL;
echo '<checkstyle version="'.Config::VERSION.'">'.PHP_EOL;
echo $cachedData;
echo '</checkstyle>'.PHP_EOL;
}//end generate()
}//end class

View File

@ -0,0 +1,362 @@
<?php
/**
* Full report for PHP_CodeSniffer.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util;
class Code implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
if ($report['errors'] === 0 && $report['warnings'] === 0) {
// Nothing to print.
return false;
}
// How many lines to show about and below the error line.
$surroundingLines = 2;
$file = $report['filename'];
$tokens = $phpcsFile->getTokens();
if (empty($tokens) === true) {
if (PHP_CODESNIFFER_VERBOSITY === 1) {
$startTime = microtime(true);
echo 'CODE report is parsing '.basename($file).' ';
} else if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "CODE report is forcing parse of $file".PHP_EOL;
}
try {
$phpcsFile->parse();
} catch (\Exception $e) {
// This is a second parse, so ignore exceptions.
// They would have been added to the file's error list already.
}
if (PHP_CODESNIFFER_VERBOSITY === 1) {
$timeTaken = ((microtime(true) - $startTime) * 1000);
if ($timeTaken < 1000) {
$timeTaken = round($timeTaken);
echo "DONE in {$timeTaken}ms";
} else {
$timeTaken = round(($timeTaken / 1000), 2);
echo "DONE in $timeTaken secs";
}
echo PHP_EOL;
}
$tokens = $phpcsFile->getTokens();
}//end if
// Create an array that maps lines to the first token on the line.
$lineTokens = [];
$lastLine = 0;
$stackPtr = 0;
foreach ($tokens as $stackPtr => $token) {
if ($token['line'] !== $lastLine) {
if ($lastLine > 0) {
$lineTokens[$lastLine]['end'] = ($stackPtr - 1);
}
$lastLine++;
$lineTokens[$lastLine] = [
'start' => $stackPtr,
'end' => null,
];
}
}
// Make sure the last token in the file sits on an imaginary
// last line so it is easier to generate code snippets at the
// end of the file.
$lineTokens[$lastLine]['end'] = $stackPtr;
// Determine the longest code line we will be showing.
$maxSnippetLength = 0;
$eolLen = strlen($phpcsFile->eolChar);
foreach ($report['messages'] as $line => $lineErrors) {
$startLine = max(($line - $surroundingLines), 1);
$endLine = min(($line + $surroundingLines), $lastLine);
$maxLineNumLength = strlen($endLine);
for ($i = $startLine; $i <= $endLine; $i++) {
if ($i === 1) {
continue;
}
$lineLength = ($tokens[($lineTokens[$i]['start'] - 1)]['column'] + $tokens[($lineTokens[$i]['start'] - 1)]['length'] - $eolLen);
$maxSnippetLength = max($lineLength, $maxSnippetLength);
}
}
$maxSnippetLength += ($maxLineNumLength + 8);
// Determine the longest error message we will be showing.
$maxErrorLength = 0;
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$length = strlen($error['message']);
if ($showSources === true) {
$length += (strlen($error['source']) + 3);
}
$maxErrorLength = max($maxErrorLength, ($length + 1));
}
}
}
// The padding that all lines will require that are printing an error message overflow.
if ($report['warnings'] > 0) {
$typeLength = 7;
} else {
$typeLength = 5;
}
$errorPadding = str_repeat(' ', ($maxLineNumLength + 7));
$errorPadding .= str_repeat(' ', $typeLength);
$errorPadding .= ' ';
if ($report['fixable'] > 0) {
$errorPadding .= ' ';
}
$errorPaddingLength = strlen($errorPadding);
// The maximum amount of space an error message can use.
$maxErrorSpace = ($width - $errorPaddingLength);
if ($showSources === true) {
// Account for the chars used to print colors.
$maxErrorSpace += 8;
}
// Figure out the max report width we need and can use.
$fileLength = strlen($file);
$maxWidth = max(($fileLength + 6), ($maxErrorLength + $errorPaddingLength));
$width = max(min($width, $maxWidth), $maxSnippetLength);
if ($width < 70) {
$width = 70;
}
// Print the file header.
echo PHP_EOL."\033[1mFILE: ";
if ($fileLength <= ($width - 6)) {
echo $file;
} else {
echo '...'.substr($file, ($fileLength - ($width - 6)));
}
echo "\033[0m".PHP_EOL;
echo str_repeat('-', $width).PHP_EOL;
echo "\033[1m".'FOUND '.$report['errors'].' ERROR';
if ($report['errors'] !== 1) {
echo 'S';
}
if ($report['warnings'] > 0) {
echo ' AND '.$report['warnings'].' WARNING';
if ($report['warnings'] !== 1) {
echo 'S';
}
}
echo ' AFFECTING '.count($report['messages']).' LINE';
if (count($report['messages']) !== 1) {
echo 'S';
}
echo "\033[0m".PHP_EOL;
foreach ($report['messages'] as $line => $lineErrors) {
$startLine = max(($line - $surroundingLines), 1);
$endLine = min(($line + $surroundingLines), $lastLine);
$snippet = '';
if (isset($lineTokens[$startLine]) === true) {
for ($i = $lineTokens[$startLine]['start']; $i <= $lineTokens[$endLine]['end']; $i++) {
$snippetLine = $tokens[$i]['line'];
if ($lineTokens[$snippetLine]['start'] === $i) {
// Starting a new line.
if ($snippetLine === $line) {
$snippet .= "\033[1m".'>> ';
} else {
$snippet .= ' ';
}
$snippet .= str_repeat(' ', ($maxLineNumLength - strlen($snippetLine)));
$snippet .= $snippetLine.': ';
if ($snippetLine === $line) {
$snippet .= "\033[0m";
}
}
if (isset($tokens[$i]['orig_content']) === true) {
$tokenContent = $tokens[$i]['orig_content'];
} else {
$tokenContent = $tokens[$i]['content'];
}
if (strpos($tokenContent, "\t") !== false) {
$token = $tokens[$i];
$token['content'] = $tokenContent;
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$tab = "\000";
} else {
$tab = "\033[30;1m»\033[0m";
}
$phpcsFile->tokenizer->replaceTabsInToken($token, $tab, "\000");
$tokenContent = $token['content'];
}
$tokenContent = Util\Common::prepareForOutput($tokenContent, ["\r", "\n", "\t"]);
$tokenContent = str_replace("\000", ' ', $tokenContent);
$underline = false;
if ($snippetLine === $line && isset($lineErrors[$tokens[$i]['column']]) === true) {
$underline = true;
}
// Underline invisible characters as well.
if ($underline === true && trim($tokenContent) === '') {
$snippet .= "\033[4m".' '."\033[0m".$tokenContent;
} else {
if ($underline === true) {
$snippet .= "\033[4m";
}
$snippet .= $tokenContent;
if ($underline === true) {
$snippet .= "\033[0m";
}
}
}//end for
}//end if
echo str_repeat('-', $width).PHP_EOL;
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$padding = ($maxLineNumLength - strlen($line));
echo 'LINE '.str_repeat(' ', $padding).$line.': ';
if ($error['type'] === 'ERROR') {
echo "\033[31mERROR\033[0m";
if ($report['warnings'] > 0) {
echo ' ';
}
} else {
echo "\033[33mWARNING\033[0m";
}
echo ' ';
if ($report['fixable'] > 0) {
echo '[';
if ($error['fixable'] === true) {
echo 'x';
} else {
echo ' ';
}
echo '] ';
}
$message = $error['message'];
$message = str_replace("\n", "\n".$errorPadding, $message);
if ($showSources === true) {
$message = "\033[1m".$message."\033[0m".' ('.$error['source'].')';
}
$errorMsg = wordwrap(
$message,
$maxErrorSpace,
PHP_EOL.$errorPadding
);
echo $errorMsg.PHP_EOL;
}//end foreach
}//end foreach
echo str_repeat('-', $width).PHP_EOL;
echo rtrim($snippet).PHP_EOL;
}//end foreach
echo str_repeat('-', $width).PHP_EOL;
if ($report['fixable'] > 0) {
echo "\033[1m".'PHPCBF CAN FIX THE '.$report['fixable'].' MARKED SNIFF VIOLATIONS AUTOMATICALLY'."\033[0m".PHP_EOL;
echo str_repeat('-', $width).PHP_EOL;
}
return true;
}//end generateFileReport()
/**
* Prints all errors and warnings for each file processed.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
if ($cachedData === '') {
return;
}
echo $cachedData;
if ($toScreen === true && $interactive === false) {
Util\Timing::printRunTime();
}
}//end generate()
}//end class

View File

@ -0,0 +1,91 @@
<?php
/**
* CSV report for PHP_CodeSniffer.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
class Csv implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
if ($report['errors'] === 0 && $report['warnings'] === 0) {
// Nothing to print.
return false;
}
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$filename = str_replace('"', '\"', $report['filename']);
$message = str_replace('"', '\"', $error['message']);
$type = strtolower($error['type']);
$source = $error['source'];
$severity = $error['severity'];
$fixable = (int) $error['fixable'];
echo "\"$filename\",$line,$column,$type,\"$message\",$source,$severity,$fixable".PHP_EOL;
}
}
}
return true;
}//end generateFileReport()
/**
* Generates a csv report.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
echo 'File,Line,Column,Type,Message,Source,Severity,Fixable'.PHP_EOL;
echo $cachedData;
}//end generate()
}//end class

View File

@ -0,0 +1,131 @@
<?php
/**
* Diff report for PHP_CodeSniffer.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util;
class Diff implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
$errors = $phpcsFile->getFixableCount();
if ($errors === 0) {
return false;
}
$phpcsFile->disableCaching();
$tokens = $phpcsFile->getTokens();
if (empty($tokens) === true) {
if (PHP_CODESNIFFER_VERBOSITY === 1) {
$startTime = microtime(true);
echo 'DIFF report is parsing '.basename($report['filename']).' ';
} else if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo 'DIFF report is forcing parse of '.$report['filename'].PHP_EOL;
}
$phpcsFile->parse();
if (PHP_CODESNIFFER_VERBOSITY === 1) {
$timeTaken = ((microtime(true) - $startTime) * 1000);
if ($timeTaken < 1000) {
$timeTaken = round($timeTaken);
echo "DONE in {$timeTaken}ms";
} else {
$timeTaken = round(($timeTaken / 1000), 2);
echo "DONE in $timeTaken secs";
}
echo PHP_EOL;
}
$phpcsFile->fixer->startFile($phpcsFile);
}//end if
if (PHP_CODESNIFFER_VERBOSITY > 1) {
ob_end_clean();
echo "\t*** START FILE FIXING ***".PHP_EOL;
}
$fixed = $phpcsFile->fixer->fixFile();
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "\t*** END FILE FIXING ***".PHP_EOL;
ob_start();
}
if ($fixed === false) {
return false;
}
$diff = $phpcsFile->fixer->generateDiff();
if ($diff === '') {
// Nothing to print.
return false;
}
echo $diff.PHP_EOL;
return true;
}//end generateFileReport()
/**
* Prints all errors and warnings for each file processed.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
echo $cachedData;
if ($toScreen === true && $cachedData !== '') {
echo PHP_EOL;
}
}//end generate()
}//end class

View File

@ -0,0 +1,90 @@
<?php
/**
* Emacs report for PHP_CodeSniffer.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
class Emacs implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
if ($report['errors'] === 0 && $report['warnings'] === 0) {
// Nothing to print.
return false;
}
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$message = $error['message'];
if ($showSources === true) {
$message .= ' ('.$error['source'].')';
}
$type = strtolower($error['type']);
echo $report['filename'].':'.$line.':'.$column.': '.$type.' - '.$message.PHP_EOL;
}
}
}
return true;
}//end generateFileReport()
/**
* Generates an emacs report.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
echo $cachedData;
}//end generate()
}//end class

View File

@ -0,0 +1,218 @@
<?php
/**
* Full report for PHP_CodeSniffer.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util;
class Full implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
if ($report['errors'] === 0 && $report['warnings'] === 0) {
// Nothing to print.
return false;
}
// The length of the word ERROR or WARNING; used for padding.
if ($report['warnings'] > 0) {
$typeLength = 7;
} else {
$typeLength = 5;
}
// Work out the max line number length for formatting.
$maxLineNumLength = max(array_map('strlen', array_keys($report['messages'])));
// The padding that all lines will require that are
// printing an error message overflow.
$paddingLine2 = str_repeat(' ', ($maxLineNumLength + 1));
$paddingLine2 .= ' | ';
$paddingLine2 .= str_repeat(' ', $typeLength);
$paddingLine2 .= ' | ';
if ($report['fixable'] > 0) {
$paddingLine2 .= ' ';
}
$paddingLength = strlen($paddingLine2);
// Make sure the report width isn't too big.
$maxErrorLength = 0;
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$length = strlen($error['message']);
if ($showSources === true) {
$length += (strlen($error['source']) + 3);
}
$maxErrorLength = max($maxErrorLength, ($length + 1));
}
}
}
$file = $report['filename'];
$fileLength = strlen($file);
$maxWidth = max(($fileLength + 6), ($maxErrorLength + $paddingLength));
$width = min($width, $maxWidth);
if ($width < 70) {
$width = 70;
}
echo PHP_EOL."\033[1mFILE: ";
if ($fileLength <= ($width - 6)) {
echo $file;
} else {
echo '...'.substr($file, ($fileLength - ($width - 6)));
}
echo "\033[0m".PHP_EOL;
echo str_repeat('-', $width).PHP_EOL;
echo "\033[1m".'FOUND '.$report['errors'].' ERROR';
if ($report['errors'] !== 1) {
echo 'S';
}
if ($report['warnings'] > 0) {
echo ' AND '.$report['warnings'].' WARNING';
if ($report['warnings'] !== 1) {
echo 'S';
}
}
echo ' AFFECTING '.count($report['messages']).' LINE';
if (count($report['messages']) !== 1) {
echo 'S';
}
echo "\033[0m".PHP_EOL;
echo str_repeat('-', $width).PHP_EOL;
// The maximum amount of space an error message can use.
$maxErrorSpace = ($width - $paddingLength - 1);
if ($showSources === true) {
// Account for the chars used to print colors.
$maxErrorSpace += 8;
}
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$message = $error['message'];
$message = str_replace("\n", "\n".$paddingLine2, $message);
if ($showSources === true) {
$message = "\033[1m".$message."\033[0m".' ('.$error['source'].')';
}
// The padding that goes on the front of the line.
$padding = ($maxLineNumLength - strlen($line));
$errorMsg = wordwrap(
$message,
$maxErrorSpace,
PHP_EOL.$paddingLine2
);
echo ' '.str_repeat(' ', $padding).$line.' | ';
if ($error['type'] === 'ERROR') {
echo "\033[31mERROR\033[0m";
if ($report['warnings'] > 0) {
echo ' ';
}
} else {
echo "\033[33mWARNING\033[0m";
}
echo ' | ';
if ($report['fixable'] > 0) {
echo '[';
if ($error['fixable'] === true) {
echo 'x';
} else {
echo ' ';
}
echo '] ';
}
echo $errorMsg.PHP_EOL;
}//end foreach
}//end foreach
}//end foreach
echo str_repeat('-', $width).PHP_EOL;
if ($report['fixable'] > 0) {
echo "\033[1m".'PHPCBF CAN FIX THE '.$report['fixable'].' MARKED SNIFF VIOLATIONS AUTOMATICALLY'."\033[0m".PHP_EOL;
echo str_repeat('-', $width).PHP_EOL;
}
echo PHP_EOL;
return true;
}//end generateFileReport()
/**
* Prints all errors and warnings for each file processed.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
if ($cachedData === '') {
return;
}
echo $cachedData;
if ($toScreen === true && $interactive === false) {
Util\Timing::printRunTime();
}
}//end generate()
}//end class

View File

@ -0,0 +1,90 @@
<?php
/**
* Git blame report for PHP_CodeSniffer.
*
* @author Ben Selby <benmatselby@gmail.com>
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Exceptions\DeepExitException;
class Gitblame extends VersionControl
{
/**
* The name of the report we want in the output
*
* @var string
*/
protected $reportName = 'GIT';
/**
* Extract the author from a blame line.
*
* @param string $line Line to parse.
*
* @return mixed string or false if impossible to recover.
*/
protected function getAuthor($line)
{
$blameParts = [];
$line = preg_replace('|\s+|', ' ', $line);
preg_match(
'|\(.+[0-9]{4}-[0-9]{2}-[0-9]{2}\s+[0-9]+\)|',
$line,
$blameParts
);
if (isset($blameParts[0]) === false) {
return false;
}
$parts = explode(' ', $blameParts[0]);
if (count($parts) < 2) {
return false;
}
$parts = array_slice($parts, 0, (count($parts) - 2));
$author = preg_replace('|\(|', '', implode($parts, ' '));
return $author;
}//end getAuthor()
/**
* Gets the blame output.
*
* @param string $filename File to blame.
*
* @return array
*/
protected function getBlameContent($filename)
{
$cwd = getcwd();
chdir(dirname($filename));
$command = 'git blame --date=short "'.$filename.'" 2>&1';
$handle = popen($command, 'r');
if ($handle === false) {
$error = 'ERROR: Could not execute "'.$command.'"'.PHP_EOL.PHP_EOL;
throw new DeepExitException($error, 3);
}
$rawContent = stream_get_contents($handle);
fclose($handle);
$blames = explode("\n", $rawContent);
chdir($cwd);
return $blames;
}//end getBlameContent()
}//end class

View File

@ -0,0 +1,109 @@
<?php
/**
* Mercurial blame report for PHP_CodeSniffer.
*
* @author Ben Selby <benmatselby@gmail.com>
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Exceptions\DeepExitException;
class Hgblame extends VersionControl
{
/**
* The name of the report we want in the output
*
* @var string
*/
protected $reportName = 'MERCURIAL';
/**
* Extract the author from a blame line.
*
* @param string $line Line to parse.
*
* @return mixed string or false if impossible to recover.
*/
protected function getAuthor($line)
{
$blameParts = [];
$line = preg_replace('|\s+|', ' ', $line);
preg_match(
'|(.+[0-9]{2}:[0-9]{2}:[0-9]{2}\s[0-9]{4}\s.[0-9]{4}:)|',
$line,
$blameParts
);
if (isset($blameParts[0]) === false) {
return false;
}
$parts = explode(' ', $blameParts[0]);
if (count($parts) < 6) {
return false;
}
$parts = array_slice($parts, 0, (count($parts) - 6));
return trim(preg_replace('|<.+>|', '', implode($parts, ' ')));
}//end getAuthor()
/**
* Gets the blame output.
*
* @param string $filename File to blame.
*
* @return array
*/
protected function getBlameContent($filename)
{
$cwd = getcwd();
$fileParts = explode(DIRECTORY_SEPARATOR, $filename);
$found = false;
$location = '';
while (empty($fileParts) === false) {
array_pop($fileParts);
$location = implode($fileParts, DIRECTORY_SEPARATOR);
if (is_dir($location.DIRECTORY_SEPARATOR.'.hg') === true) {
$found = true;
break;
}
}
if ($found === true) {
chdir($location);
} else {
$error = 'ERROR: Could not locate .hg directory '.PHP_EOL.PHP_EOL;
throw new DeepExitException($error, 3);
}
$command = 'hg blame -u -d -v "'.$filename.'" 2>&1';
$handle = popen($command, 'r');
if ($handle === false) {
$error = 'ERROR: Could not execute "'.$command.'"'.PHP_EOL.PHP_EOL;
throw new DeepExitException($error, 3);
}
$rawContent = stream_get_contents($handle);
fclose($handle);
$blames = explode("\n", $rawContent);
chdir($cwd);
return $blames;
}//end getBlameContent()
}//end class

View File

@ -0,0 +1,141 @@
<?php
/**
* Info report for PHP_CodeSniffer.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Timing;
class Info implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
$metrics = $phpcsFile->getMetrics();
foreach ($metrics as $metric => $data) {
foreach ($data['values'] as $value => $count) {
echo "$metric>>$value>>$count".PHP_EOL;
}
}
return true;
}//end generateFileReport()
/**
* Prints the source of all errors and warnings.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
$lines = explode(PHP_EOL, $cachedData);
array_pop($lines);
if (empty($lines) === true) {
return;
}
$metrics = [];
foreach ($lines as $line) {
$parts = explode('>>', $line);
$metric = $parts[0];
$value = $parts[1];
$count = $parts[2];
if (isset($metrics[$metric]) === false) {
$metrics[$metric] = [];
}
if (isset($metrics[$metric][$value]) === false) {
$metrics[$metric][$value] = $count;
} else {
$metrics[$metric][$value] += $count;
}
}
ksort($metrics);
echo PHP_EOL."\033[1m".'PHP CODE SNIFFER INFORMATION REPORT'."\033[0m".PHP_EOL;
echo str_repeat('-', 70).PHP_EOL;
foreach ($metrics as $metric => $values) {
$winner = '';
$winnerCount = 0;
$totalCount = 0;
foreach ($values as $value => $count) {
$totalCount += $count;
if ($count > $winnerCount) {
$winner = $value;
$winnerCount = $count;
}
}
$winPercent = round(($winnerCount / $totalCount * 100), 2);
echo "$metric: \033[4m$winner\033[0m [$winnerCount/$totalCount, $winPercent%]".PHP_EOL;
asort($values);
$values = array_reverse($values, true);
foreach ($values as $value => $count) {
if ($value === $winner) {
continue;
}
$percent = round(($count / $totalCount * 100), 2);
echo "\t$value => $count ($percent%)".PHP_EOL;
}
echo PHP_EOL;
}//end foreach
echo str_repeat('-', 70).PHP_EOL;
if ($toScreen === true && $interactive === false) {
Timing::printRunTime();
}
}//end generate()
}//end class

View File

@ -0,0 +1,111 @@
<?php
/**
* JSON report for PHP_CodeSniffer.
*
* @author Jeffrey Fisher <jeffslofish@gmail.com>
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
class Json implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
$filename = str_replace('\\', '\\\\', $report['filename']);
$filename = str_replace('"', '\"', $filename);
$filename = str_replace('/', '\/', $filename);
echo '"'.$filename.'":{';
echo '"errors":'.$report['errors'].',"warnings":'.$report['warnings'].',"messages":[';
$messages = '';
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$error['message'] = str_replace('\\', '\\\\', $error['message']);
$error['message'] = str_replace('"', '\"', $error['message']);
$error['message'] = str_replace('/', '\/', $error['message']);
$error['message'] = str_replace("\n", '\n', $error['message']);
$error['message'] = str_replace("\r", '\r', $error['message']);
$error['message'] = str_replace("\t", '\t', $error['message']);
$fixable = 'false';
if ($error['fixable'] === true) {
$fixable = 'true';
}
$messages .= '{"message":"'.$error['message'].'",';
$messages .= '"source":"'.$error['source'].'",';
$messages .= '"severity":'.$error['severity'].',';
$messages .= '"type":"'.$error['type'].'",';
$messages .= '"line":'.$line.',';
$messages .= '"column":'.$column.',';
$messages .= '"fixable":'.$fixable;
$messages .= '},';
}//end foreach
}//end foreach
}//end foreach
echo rtrim($messages, ',');
echo ']},';
return true;
}//end generateFileReport()
/**
* Generates a JSON report.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
echo '{"totals":{"errors":'.$totalErrors.',"warnings":'.$totalWarnings.',"fixable":'.$totalFixable.'},"files":{';
echo rtrim($cachedData, ',');
echo "}}";
}//end generate()
}//end class

View File

@ -0,0 +1,130 @@
<?php
/**
* JUnit report for PHP_CodeSniffer.
*
* @author Oleg Lobach <oleg@lobach.info>
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\File;
class Junit implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
$out = new \XMLWriter;
$out->openMemory();
$out->setIndent(true);
$out->startElement('testsuite');
$out->writeAttribute('name', $report['filename']);
if (count($report['messages']) === 0) {
$out->writeAttribute('tests', 1);
$out->writeAttribute('failures', 0);
$out->startElement('testcase');
$out->writeAttribute('name', $report['filename']);
$out->endElement();
} else {
$failures = ($report['errors'] + $report['warnings']);
$out->writeAttribute('tests', $failures);
$out->writeAttribute('failures', $failures);
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$out->startElement('testcase');
$out->writeAttribute('name', $error['source'].' at '.$report['filename']." ($line:$column)");
$error['type'] = strtolower($error['type']);
if ($phpcsFile->config->encoding !== 'utf-8') {
$error['message'] = iconv($phpcsFile->config->encoding, 'utf-8', $error['message']);
}
$out->startElement('failure');
$out->writeAttribute('type', $error['type']);
$out->writeAttribute('message', $error['message']);
$out->endElement();
$out->endElement();
}
}
}
}//end if
$out->endElement();
echo $out->flush();
return true;
}//end generateFileReport()
/**
* Prints all violations for processed files, in a proprietary XML format.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
// Figure out the total number of tests.
$tests = 0;
$matches = [];
preg_match_all('/tests="([0-9]+)"/', $cachedData, $matches);
if (isset($matches[1]) === true) {
foreach ($matches[1] as $match) {
$tests += $match;
}
}
$failures = ($totalErrors + $totalWarnings);
echo '<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL;
echo '<testsuites name="PHP_CodeSniffer '.Config::VERSION.'" tests="'.$tests.'" failures="'.$failures.'">'.PHP_EOL;
echo $cachedData;
echo '</testsuites>'.PHP_EOL;
}//end generate()
}//end class

View File

@ -0,0 +1,241 @@
<?php
/**
* Notify-send report for PHP_CodeSniffer.
*
* Supported configuration parameters:
* - notifysend_path - Full path to notify-send cli command
* - notifysend_timeout - Timeout in milliseconds
* - notifysend_showok - Show "ok, all fine" messages (0/1)
*
* @author Christian Weiske <christian.weiske@netresearch.de>
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2012-2014 Christian Weiske
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\File;
class Notifysend implements Report
{
/**
* Notification timeout in milliseconds.
*
* @var integer
*/
protected $timeout = 3000;
/**
* Path to notify-send command.
*
* @var string
*/
protected $path = 'notify-send';
/**
* Show "ok, all fine" messages.
*
* @var boolean
*/
protected $showOk = true;
/**
* Version of installed notify-send executable.
*
* @var string
*/
protected $version = null;
/**
* Load configuration data.
*/
public function __construct()
{
$path = Config::getExecutablePath('notifysend');
if ($path !== null) {
$this->path = escapeshellcmd($path);
}
$timeout = Config::getConfigData('notifysend_timeout');
if ($timeout !== null) {
$this->timeout = (int) $timeout;
}
$showOk = Config::getConfigData('notifysend_showok');
if ($showOk !== null) {
$this->showOk = (boolean) $showOk;
}
$this->version = str_replace(
'notify-send ',
'',
exec($this->path.' --version')
);
}//end __construct()
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
echo $report['filename'].PHP_EOL;
// We want this file counted in the total number
// of checked files even if it has no errors.
return true;
}//end generateFileReport()
/**
* Generates a summary of errors and warnings for each file processed.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
$checkedFiles = explode(PHP_EOL, trim($cachedData));
$msg = $this->generateMessage($checkedFiles, $totalErrors, $totalWarnings);
if ($msg === null) {
if ($this->showOk === true) {
$this->notifyAllFine();
}
} else {
$this->notifyErrors($msg);
}
}//end generate()
/**
* Generate the error message to show to the user.
*
* @param string[] $checkedFiles The files checked during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
*
* @return string Error message or NULL if no error/warning found.
*/
protected function generateMessage($checkedFiles, $totalErrors, $totalWarnings)
{
if ($totalErrors === 0 && $totalWarnings === 0) {
// Nothing to print.
return null;
}
$totalFiles = count($checkedFiles);
$msg = '';
if ($totalFiles > 1) {
$msg .= 'Checked '.$totalFiles.' files'.PHP_EOL;
} else {
$msg .= $checkedFiles[0].PHP_EOL;
}
if ($totalWarnings > 0) {
$msg .= $totalWarnings.' warnings'.PHP_EOL;
}
if ($totalErrors > 0) {
$msg .= $totalErrors.' errors'.PHP_EOL;
}
return $msg;
}//end generateMessage()
/**
* Tell the user that all is fine and no error/warning has been found.
*
* @return void
*/
protected function notifyAllFine()
{
$cmd = $this->getBasicCommand();
$cmd .= ' -i info';
$cmd .= ' "PHP CodeSniffer: Ok"';
$cmd .= ' "All fine"';
exec($cmd);
}//end notifyAllFine()
/**
* Tell the user that errors/warnings have been found.
*
* @param string $msg Message to display.
*
* @return void
*/
protected function notifyErrors($msg)
{
$cmd = $this->getBasicCommand();
$cmd .= ' -i error';
$cmd .= ' "PHP CodeSniffer: Error"';
$cmd .= ' '.escapeshellarg(trim($msg));
exec($cmd);
}//end notifyErrors()
/**
* Generate and return the basic notify-send command string to execute.
*
* @return string Shell command with common parameters.
*/
protected function getBasicCommand()
{
$cmd = $this->path;
$cmd .= ' --category dev.validate';
$cmd .= ' -h int:transient:1';
$cmd .= ' -t '.(int) $this->timeout;
if (version_compare($this->version, '0.7.3', '>=') === true) {
$cmd .= ' -a phpcs';
}
return $cmd;
}//end getBasicCommand()
}//end class

View File

@ -0,0 +1,64 @@
<?php
/**
* An interface that PHP_CodeSniffer reports must implement.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
interface Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80);
/**
* Generate the actual report.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
);
}//end interface

View File

@ -0,0 +1,337 @@
<?php
/**
* Source report for PHP_CodeSniffer.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Timing;
class Source implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
if ($report['errors'] === 0 && $report['warnings'] === 0) {
// Nothing to print.
return false;
}
$sources = [];
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$src = $error['source'];
if (isset($sources[$src]) === false) {
$sources[$src] = [
'fixable' => (int) $error['fixable'],
'count' => 1,
];
} else {
$sources[$src]['count']++;
}
}
}
}
foreach ($sources as $source => $data) {
echo $source.'>>'.$data['fixable'].'>>'.$data['count'].PHP_EOL;
}
return true;
}//end generateFileReport()
/**
* Prints the source of all errors and warnings.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
$lines = explode(PHP_EOL, $cachedData);
array_pop($lines);
if (empty($lines) === true) {
return;
}
$sources = [];
$maxLength = 0;
foreach ($lines as $line) {
$parts = explode('>>', $line);
$source = $parts[0];
$fixable = (bool) $parts[1];
$count = $parts[2];
if (isset($sources[$source]) === false) {
if ($showSources === true) {
$parts = null;
$sniff = $source;
} else {
$parts = explode('.', $source);
if ($parts[0] === 'Internal') {
$parts[2] = $parts[1];
$parts[1] = '';
}
$parts[1] = $this->makeFriendlyName($parts[1]);
$sniff = $this->makeFriendlyName($parts[2]);
if (isset($parts[3]) === true) {
$name = $this->makeFriendlyName($parts[3]);
$name[0] = strtolower($name[0]);
$sniff .= ' '.$name;
unset($parts[3]);
}
$parts[2] = $sniff;
}//end if
$maxLength = max($maxLength, strlen($sniff));
$sources[$source] = [
'count' => $count,
'fixable' => $fixable,
'parts' => $parts,
];
} else {
$sources[$source]['count'] += $count;
}//end if
$fileLen = strlen($parts[0]);
$reportFiles[$parts[0]] = [
'errors' => $parts[1],
'warnings' => $parts[2],
'strlen' => $fileLen,
];
}//end foreach
if ($showSources === true) {
$width = min($width, ($maxLength + 11));
} else {
$width = min($width, ($maxLength + 41));
}
$width = max($width, 70);
asort($sources);
$sources = array_reverse($sources);
echo PHP_EOL."\033[1mPHP CODE SNIFFER VIOLATION SOURCE SUMMARY\033[0m".PHP_EOL;
echo str_repeat('-', $width).PHP_EOL."\033[1m";
if ($showSources === true) {
if ($totalFixable > 0) {
echo ' SOURCE'.str_repeat(' ', ($width - 15)).'COUNT'.PHP_EOL;
} else {
echo 'SOURCE'.str_repeat(' ', ($width - 11)).'COUNT'.PHP_EOL;
}
} else {
if ($totalFixable > 0) {
echo ' STANDARD CATEGORY SNIFF'.str_repeat(' ', ($width - 44)).'COUNT'.PHP_EOL;
} else {
echo 'STANDARD CATEGORY SNIFF'.str_repeat(' ', ($width - 40)).'COUNT'.PHP_EOL;
}
}
echo "\033[0m".str_repeat('-', $width).PHP_EOL;
$fixableSources = 0;
if ($showSources === true) {
$maxSniffWidth = ($width - 7);
} else {
$maxSniffWidth = ($width - 37);
}
if ($totalFixable > 0) {
$maxSniffWidth -= 4;
}
foreach ($sources as $source => $sourceData) {
if ($totalFixable > 0) {
echo '[';
if ($sourceData['fixable'] === true) {
echo 'x';
$fixableSources++;
} else {
echo ' ';
}
echo '] ';
}
if ($showSources === true) {
if (strlen($source) > $maxSniffWidth) {
$source = substr($source, 0, $maxSniffWidth);
}
echo $source;
if ($totalFixable > 0) {
echo str_repeat(' ', ($width - 9 - strlen($source)));
} else {
echo str_repeat(' ', ($width - 5 - strlen($source)));
}
} else {
$parts = $sourceData['parts'];
if (strlen($parts[0]) > 8) {
$parts[0] = substr($parts[0], 0, ((strlen($parts[0]) - 8) * -1));
}
echo $parts[0].str_repeat(' ', (10 - strlen($parts[0])));
$category = $parts[1];
if (strlen($category) > 18) {
$category = substr($category, 0, ((strlen($category) - 18) * -1));
}
echo $category.str_repeat(' ', (20 - strlen($category)));
$sniff = $parts[2];
if (strlen($sniff) > $maxSniffWidth) {
$sniff = substr($sniff, 0, $maxSniffWidth);
}
if ($totalFixable > 0) {
echo $sniff.str_repeat(' ', ($width - 39 - strlen($sniff)));
} else {
echo $sniff.str_repeat(' ', ($width - 35 - strlen($sniff)));
}
}//end if
echo $sourceData['count'].PHP_EOL;
}//end foreach
echo str_repeat('-', $width).PHP_EOL;
echo "\033[1m".'A TOTAL OF '.($totalErrors + $totalWarnings).' SNIFF VIOLATION';
if (($totalErrors + $totalWarnings) > 1) {
echo 'S';
}
echo ' WERE FOUND IN '.count($sources).' SOURCE';
if (count($sources) !== 1) {
echo 'S';
}
echo "\033[0m";
if ($totalFixable > 0) {
echo PHP_EOL.str_repeat('-', $width).PHP_EOL;
echo "\033[1mPHPCBF CAN FIX THE $fixableSources MARKED SOURCES AUTOMATICALLY ($totalFixable VIOLATIONS IN TOTAL)\033[0m";
}
echo PHP_EOL.str_repeat('-', $width).PHP_EOL.PHP_EOL;
if ($toScreen === true && $interactive === false) {
Timing::printRunTime();
}
}//end generate()
/**
* Converts a camel caps name into a readable string.
*
* @param string $name The camel caps name to convert.
*
* @return string
*/
public function makeFriendlyName($name)
{
if (trim($name) === '') {
return '';
}
$friendlyName = '';
$length = strlen($name);
$lastWasUpper = false;
$lastWasNumeric = false;
for ($i = 0; $i < $length; $i++) {
if (is_numeric($name[$i]) === true) {
if ($lastWasNumeric === false) {
$friendlyName .= ' ';
}
$lastWasUpper = false;
$lastWasNumeric = true;
} else {
$lastWasNumeric = false;
$char = strtolower($name[$i]);
if ($char === $name[$i]) {
// Lowercase.
$lastWasUpper = false;
} else {
// Uppercase.
if ($lastWasUpper === false) {
$friendlyName .= ' ';
if ($i < ($length - 1)) {
$next = $name[($i + 1)];
if (strtolower($next) === $next) {
// Next char is lowercase so it is a word boundary.
$name[$i] = strtolower($name[$i]);
}
}
}
$lastWasUpper = true;
}
}//end if
$friendlyName .= $name[$i];
}//end for
$friendlyName = trim($friendlyName);
$friendlyName[0] = strtoupper($friendlyName[0]);
return $friendlyName;
}//end makeFriendlyName()
}//end class

View File

@ -0,0 +1,162 @@
<?php
/**
* Summary report for PHP_CodeSniffer.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util;
class Summary implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
if (PHP_CODESNIFFER_VERBOSITY === 0
&& $report['errors'] === 0
&& $report['warnings'] === 0
) {
// Nothing to print.
return false;
}
echo $report['filename'].'>>'.$report['errors'].'>>'.$report['warnings'].PHP_EOL;
return true;
}//end generateFileReport()
/**
* Generates a summary of errors and warnings for each file processed.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
$lines = explode(PHP_EOL, $cachedData);
array_pop($lines);
if (empty($lines) === true) {
return;
}
$reportFiles = [];
$maxLength = 0;
foreach ($lines as $line) {
$parts = explode('>>', $line);
$fileLen = strlen($parts[0]);
$reportFiles[$parts[0]] = [
'errors' => $parts[1],
'warnings' => $parts[2],
'strlen' => $fileLen,
];
$maxLength = max($maxLength, $fileLen);
}
$width = min($width, ($maxLength + 21));
$width = max($width, 70);
echo PHP_EOL."\033[1m".'PHP CODE SNIFFER REPORT SUMMARY'."\033[0m".PHP_EOL;
echo str_repeat('-', $width).PHP_EOL;
echo "\033[1m".'FILE'.str_repeat(' ', ($width - 20)).'ERRORS WARNINGS'."\033[0m".PHP_EOL;
echo str_repeat('-', $width).PHP_EOL;
foreach ($reportFiles as $file => $data) {
$padding = ($width - 18 - $data['strlen']);
if ($padding < 0) {
$file = '...'.substr($file, (($padding * -1) + 3));
$padding = 0;
}
echo $file.str_repeat(' ', $padding).' ';
if ($data['errors'] !== 0) {
echo "\033[31m".$data['errors']."\033[0m";
echo str_repeat(' ', (8 - strlen((string) $data['errors'])));
} else {
echo '0 ';
}
if ($data['warnings'] !== 0) {
echo "\033[33m".$data['warnings']."\033[0m";
} else {
echo '0';
}
echo PHP_EOL;
}//end foreach
echo str_repeat('-', $width).PHP_EOL;
echo "\033[1mA TOTAL OF $totalErrors ERROR";
if ($totalErrors !== 1) {
echo 'S';
}
echo ' AND '.$totalWarnings.' WARNING';
if ($totalWarnings !== 1) {
echo 'S';
}
echo ' WERE FOUND IN '.$totalFiles.' FILE';
if ($totalFiles !== 1) {
echo 'S';
}
echo "\033[0m";
if ($totalFixable > 0) {
echo PHP_EOL.str_repeat('-', $width).PHP_EOL;
echo "\033[1mPHPCBF CAN FIX $totalFixable OF THESE SNIFF VIOLATIONS AUTOMATICALLY\033[0m";
}
echo PHP_EOL.str_repeat('-', $width).PHP_EOL.PHP_EOL;
if ($toScreen === true && $interactive === false) {
Util\Timing::printRunTime();
}
}//end generate()
}//end class

View File

@ -0,0 +1,72 @@
<?php
/**
* SVN blame report for PHP_CodeSniffer.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Exceptions\DeepExitException;
class Svnblame extends VersionControl
{
/**
* The name of the report we want in the output
*
* @var string
*/
protected $reportName = 'SVN';
/**
* Extract the author from a blame line.
*
* @param string $line Line to parse.
*
* @return mixed string or false if impossible to recover.
*/
protected function getAuthor($line)
{
$blameParts = [];
preg_match('|\s*([^\s]+)\s+([^\s]+)|', $line, $blameParts);
if (isset($blameParts[2]) === false) {
return false;
}
return $blameParts[2];
}//end getAuthor()
/**
* Gets the blame output.
*
* @param string $filename File to blame.
*
* @return array
*/
protected function getBlameContent($filename)
{
$command = 'svn blame "'.$filename.'" 2>&1';
$handle = popen($command, 'r');
if ($handle === false) {
$error = 'ERROR: Could not execute "'.$command.'"'.PHP_EOL.PHP_EOL;
throw new DeepExitException($error, 3);
}
$rawContent = stream_get_contents($handle);
fclose($handle);
$blames = explode("\n", $rawContent);
return $blames;
}//end getBlameContent()
}//end class

View File

@ -0,0 +1,376 @@
<?php
/**
* Version control report base class for PHP_CodeSniffer.
*
* @author Ben Selby <benmatselby@gmail.com>
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Timing;
abstract class VersionControl implements Report
{
/**
* The name of the report we want in the output.
*
* @var string
*/
protected $reportName = 'VERSION CONTROL';
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
$blames = $this->getBlameContent($report['filename']);
$authorCache = [];
$praiseCache = [];
$sourceCache = [];
foreach ($report['messages'] as $line => $lineErrors) {
$author = 'Unknown';
if (isset($blames[($line - 1)]) === true) {
$blameAuthor = $this->getAuthor($blames[($line - 1)]);
if ($blameAuthor !== false) {
$author = $blameAuthor;
}
}
if (isset($authorCache[$author]) === false) {
$authorCache[$author] = 0;
$praiseCache[$author] = [
'good' => 0,
'bad' => 0,
];
}
$praiseCache[$author]['bad']++;
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$authorCache[$author]++;
if ($showSources === true) {
$source = $error['source'];
if (isset($sourceCache[$author][$source]) === false) {
$sourceCache[$author][$source] = [
'count' => 1,
'fixable' => $error['fixable'],
];
} else {
$sourceCache[$author][$source]['count']++;
}
}
}
}
unset($blames[($line - 1)]);
}//end foreach
// Now go through and give the authors some credit for
// all the lines that do not have errors.
foreach ($blames as $line) {
$author = $this->getAuthor($line);
if ($author === false) {
$author = 'Unknown';
}
if (isset($authorCache[$author]) === false) {
// This author doesn't have any errors.
if (PHP_CODESNIFFER_VERBOSITY === 0) {
continue;
}
$authorCache[$author] = 0;
$praiseCache[$author] = [
'good' => 0,
'bad' => 0,
];
}
$praiseCache[$author]['good']++;
}//end foreach
foreach ($authorCache as $author => $errors) {
echo "AUTHOR>>$author>>$errors".PHP_EOL;
}
foreach ($praiseCache as $author => $praise) {
echo "PRAISE>>$author>>".$praise['good'].'>>'.$praise['bad'].PHP_EOL;
}
foreach ($sourceCache as $author => $sources) {
foreach ($sources as $source => $sourceData) {
$count = $sourceData['count'];
$fixable = (int) $sourceData['fixable'];
echo "SOURCE>>$author>>$source>>$count>>$fixable".PHP_EOL;
}
}
return true;
}//end generateFileReport()
/**
* Prints the author of all errors and warnings, as given by "version control blame".
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
$errorsShown = ($totalErrors + $totalWarnings);
if ($errorsShown === 0) {
// Nothing to show.
return;
}
$lines = explode(PHP_EOL, $cachedData);
array_pop($lines);
if (empty($lines) === true) {
return;
}
$authorCache = [];
$praiseCache = [];
$sourceCache = [];
foreach ($lines as $line) {
$parts = explode('>>', $line);
switch ($parts[0]) {
case 'AUTHOR':
if (isset($authorCache[$parts[1]]) === false) {
$authorCache[$parts[1]] = $parts[2];
} else {
$authorCache[$parts[1]] += $parts[2];
}
break;
case 'PRAISE':
if (isset($praiseCache[$parts[1]]) === false) {
$praiseCache[$parts[1]] = [
'good' => $parts[2],
'bad' => $parts[3],
];
} else {
$praiseCache[$parts[1]]['good'] += $parts[2];
$praiseCache[$parts[1]]['bad'] += $parts[3];
}
break;
case 'SOURCE':
if (isset($praiseCache[$parts[1]]) === false) {
$praiseCache[$parts[1]] = [];
}
if (isset($sourceCache[$parts[1]][$parts[2]]) === false) {
$sourceCache[$parts[1]][$parts[2]] = [
'count' => $parts[3],
'fixable' => (bool) $parts[4],
];
} else {
$sourceCache[$parts[1]][$parts[2]]['count'] += $parts[3];
}
break;
default:
break;
}//end switch
}//end foreach
// Make sure the report width isn't too big.
$maxLength = 0;
foreach ($authorCache as $author => $count) {
$maxLength = max($maxLength, strlen($author));
if ($showSources === true && isset($sourceCache[$author]) === true) {
foreach ($sourceCache[$author] as $source => $sourceData) {
if ($source === 'count') {
continue;
}
$maxLength = max($maxLength, (strlen($source) + 9));
}
}
}
$width = min($width, ($maxLength + 30));
$width = max($width, 70);
arsort($authorCache);
echo PHP_EOL."\033[1m".'PHP CODE SNIFFER '.$this->reportName.' BLAME SUMMARY'."\033[0m".PHP_EOL;
echo str_repeat('-', $width).PHP_EOL."\033[1m";
if ($showSources === true) {
echo 'AUTHOR SOURCE'.str_repeat(' ', ($width - 43)).'(Author %) (Overall %) COUNT'.PHP_EOL;
echo str_repeat('-', $width).PHP_EOL;
} else {
echo 'AUTHOR'.str_repeat(' ', ($width - 34)).'(Author %) (Overall %) COUNT'.PHP_EOL;
echo str_repeat('-', $width).PHP_EOL;
}
echo "\033[0m";
if ($showSources === true) {
$maxSniffWidth = ($width - 15);
if ($totalFixable > 0) {
$maxSniffWidth -= 4;
}
}
$fixableSources = 0;
foreach ($authorCache as $author => $count) {
if ($praiseCache[$author]['good'] === 0) {
$percent = 0;
} else {
$total = ($praiseCache[$author]['bad'] + $praiseCache[$author]['good']);
$percent = round(($praiseCache[$author]['bad'] / $total * 100), 2);
}
$overallPercent = '('.round((($count / $errorsShown) * 100), 2).')';
$authorPercent = '('.$percent.')';
$line = str_repeat(' ', (6 - strlen($count))).$count;
$line = str_repeat(' ', (12 - strlen($overallPercent))).$overallPercent.$line;
$line = str_repeat(' ', (11 - strlen($authorPercent))).$authorPercent.$line;
$line = $author.str_repeat(' ', ($width - strlen($author) - strlen($line))).$line;
if ($showSources === true) {
$line = "\033[1m$line\033[0m";
}
echo $line.PHP_EOL;
if ($showSources === true && isset($sourceCache[$author]) === true) {
$errors = $sourceCache[$author];
asort($errors);
$errors = array_reverse($errors);
foreach ($errors as $source => $sourceData) {
if ($source === 'count') {
continue;
}
$count = $sourceData['count'];
$srcLength = strlen($source);
if ($srcLength > $maxSniffWidth) {
$source = substr($source, 0, $maxSniffWidth);
}
$line = str_repeat(' ', (5 - strlen($count))).$count;
echo ' ';
if ($totalFixable > 0) {
echo '[';
if ($sourceData['fixable'] === true) {
echo 'x';
$fixableSources++;
} else {
echo ' ';
}
echo '] ';
}
echo $source;
if ($totalFixable > 0) {
echo str_repeat(' ', ($width - 18 - strlen($source)));
} else {
echo str_repeat(' ', ($width - 14 - strlen($source)));
}
echo $line.PHP_EOL;
}//end foreach
}//end if
}//end foreach
echo str_repeat('-', $width).PHP_EOL;
echo "\033[1m".'A TOTAL OF '.$errorsShown.' SNIFF VIOLATION';
if ($errorsShown !== 1) {
echo 'S';
}
echo ' WERE COMMITTED BY '.count($authorCache).' AUTHOR';
if (count($authorCache) !== 1) {
echo 'S';
}
echo "\033[0m";
if ($totalFixable > 0) {
if ($showSources === true) {
echo PHP_EOL.str_repeat('-', $width).PHP_EOL;
echo "\033[1mPHPCBF CAN FIX THE $fixableSources MARKED SOURCES AUTOMATICALLY ($totalFixable VIOLATIONS IN TOTAL)\033[0m";
} else {
echo PHP_EOL.str_repeat('-', $width).PHP_EOL;
echo "\033[1mPHPCBF CAN FIX $totalFixable OF THESE SNIFF VIOLATIONS AUTOMATICALLY\033[0m";
}
}
echo PHP_EOL.str_repeat('-', $width).PHP_EOL.PHP_EOL;
if ($toScreen === true && $interactive === false) {
Timing::printRunTime();
}
}//end generate()
/**
* Extract the author from a blame line.
*
* @param string $line Line to parse.
*
* @return mixed string or false if impossible to recover.
*/
abstract protected function getAuthor($line);
/**
* Gets the blame output.
*
* @param string $filename File to blame.
*
* @return array
*/
abstract protected function getBlameContent($filename);
}//end class

View File

@ -0,0 +1,121 @@
<?php
/**
* XML report for PHP_CodeSniffer.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\File;
class Xml implements Report
{
/**
* Generate a partial report for a single processed file.
*
* Function should return TRUE if it printed or stored data about the file
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
$out = new \XMLWriter;
$out->openMemory();
$out->setIndent(true);
$out->setIndentString(' ');
$out->startDocument('1.0', 'UTF-8');
if ($report['errors'] === 0 && $report['warnings'] === 0) {
// Nothing to print.
return false;
}
$out->startElement('file');
$out->writeAttribute('name', $report['filename']);
$out->writeAttribute('errors', $report['errors']);
$out->writeAttribute('warnings', $report['warnings']);
$out->writeAttribute('fixable', $report['fixable']);
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$error['type'] = strtolower($error['type']);
if ($phpcsFile->config->encoding !== 'utf-8') {
$error['message'] = iconv($phpcsFile->config->encoding, 'utf-8', $error['message']);
}
$out->startElement($error['type']);
$out->writeAttribute('line', $line);
$out->writeAttribute('column', $column);
$out->writeAttribute('source', $error['source']);
$out->writeAttribute('severity', $error['severity']);
$out->writeAttribute('fixable', (int) $error['fixable']);
$out->text($error['message']);
$out->endElement();
}
}
}//end foreach
$out->endElement();
// Remove the start of the document because we will
// add that manually later. We only have it in here to
// properly set the encoding.
$content = $out->flush();
$content = substr($content, (strpos($content, PHP_EOL) + strlen(PHP_EOL)));
echo $content;
return true;
}//end generateFileReport()
/**
* Prints all violations for processed files, in a proprietary XML format.
*
* @param string $cachedData Any partial report data that was returned from
* generateFileReport during the run.
* @param int $totalFiles Total number of files processed during the run.
* @param int $totalErrors Total number of errors found during the run.
* @param int $totalWarnings Total number of warnings found during the run.
* @param int $totalFixable Total number of problems that can be fixed.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param bool $interactive Are we running in interactive mode?
* @param bool $toScreen Is the report being printed to screen?
*
* @return void
*/
public function generate(
$cachedData,
$totalFiles,
$totalErrors,
$totalWarnings,
$totalFixable,
$showSources=false,
$width=80,
$interactive=false,
$toScreen=true
) {
echo '<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL;
echo '<phpcs version="'.Config::VERSION.'">'.PHP_EOL;
echo $cachedData;
echo '</phpcs>'.PHP_EOL;
}//end generate()
}//end class

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,829 @@
<?php
/**
* Responsible for running PHPCS and PHPCBF.
*
* After creating an object of this class, you probably just want to
* call runPHPCS() or runPHPCBF().
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer;
use PHP_CodeSniffer\Files\FileList;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Files\DummyFile;
use PHP_CodeSniffer\Util\Cache;
use PHP_CodeSniffer\Util\Common;
use PHP_CodeSniffer\Util\Standards;
use PHP_CodeSniffer\Exceptions\RuntimeException;
use PHP_CodeSniffer\Exceptions\DeepExitException;
class Runner
{
/**
* The config data for the run.
*
* @var \PHP_CodeSniffer\Config
*/
public $config = null;
/**
* The ruleset used for the run.
*
* @var \PHP_CodeSniffer\Ruleset
*/
public $ruleset = null;
/**
* The reporter used for generating reports after the run.
*
* @var \PHP_CodeSniffer\Reporter
*/
public $reporter = null;
/**
* Run the PHPCS script.
*
* @return array
*/
public function runPHPCS()
{
try {
Util\Timing::startTiming();
Runner::checkRequirements();
if (defined('PHP_CODESNIFFER_CBF') === false) {
define('PHP_CODESNIFFER_CBF', false);
}
// Creating the Config object populates it with all required settings
// based on the CLI arguments provided to the script and any config
// values the user has set.
$this->config = new Config();
// Init the run and load the rulesets to set additional config vars.
$this->init();
// Print a list of sniffs in each of the supplied standards.
// We fudge the config here so that each standard is explained in isolation.
if ($this->config->explain === true) {
$standards = $this->config->standards;
foreach ($standards as $standard) {
$this->config->standards = [$standard];
$ruleset = new Ruleset($this->config);
$ruleset->explain();
}
return 0;
}
// Generate documentation for each of the supplied standards.
if ($this->config->generator !== null) {
$standards = $this->config->standards;
foreach ($standards as $standard) {
$this->config->standards = [$standard];
$ruleset = new Ruleset($this->config);
$class = 'PHP_CodeSniffer\Generators\\'.$this->config->generator;
$generator = new $class($ruleset);
$generator->generate();
}
return 0;
}
// Other report formats don't really make sense in interactive mode
// so we hard-code the full report here and when outputting.
// We also ensure parallel processing is off because we need to do one file at a time.
if ($this->config->interactive === true) {
$this->config->reports = ['full' => null];
$this->config->parallel = 1;
$this->config->showProgress = false;
}
// Disable caching if we are processing STDIN as we can't be 100%
// sure where the file came from or if it will change in the future.
if ($this->config->stdin === true) {
$this->config->cache = false;
}
$numErrors = $this->run();
// Print all the reports for this run.
$toScreen = $this->reporter->printReports();
// Only print timer output if no reports were
// printed to the screen so we don't put additional output
// in something like an XML report. If we are printing to screen,
// the report types would have already worked out who should
// print the timer info.
if ($this->config->interactive === false
&& ($toScreen === false
|| (($this->reporter->totalErrors + $this->reporter->totalWarnings) === 0 && $this->config->showProgress === true))
) {
Util\Timing::printRunTime();
}
} catch (DeepExitException $e) {
echo $e->getMessage();
return $e->getCode();
}//end try
if ($numErrors === 0) {
// No errors found.
return 0;
} else if ($this->reporter->totalFixable === 0) {
// Errors found, but none of them can be fixed by PHPCBF.
return 1;
} else {
// Errors found, and some can be fixed by PHPCBF.
return 2;
}
}//end runPHPCS()
/**
* Run the PHPCBF script.
*
* @return array
*/
public function runPHPCBF()
{
if (defined('PHP_CODESNIFFER_CBF') === false) {
define('PHP_CODESNIFFER_CBF', true);
}
try {
Util\Timing::startTiming();
Runner::checkRequirements();
// Creating the Config object populates it with all required settings
// based on the CLI arguments provided to the script and any config
// values the user has set.
$this->config = new Config();
// When processing STDIN, we can't output anything to the screen
// or it will end up mixed in with the file output.
if ($this->config->stdin === true) {
$this->config->verbosity = 0;
}
// Init the run and load the rulesets to set additional config vars.
$this->init();
// Override some of the command line settings that might break the fixes.
$this->config->generator = null;
$this->config->explain = false;
$this->config->interactive = false;
$this->config->cache = false;
$this->config->showSources = false;
$this->config->recordErrors = false;
$this->config->reportFile = null;
$this->config->reports = ['cbf' => null];
// If a standard tries to set command line arguments itself, some
// may be blocked because PHPCBF is running, so stop the script
// dying if any are found.
$this->config->dieOnUnknownArg = false;
$this->run();
$this->reporter->printReports();
echo PHP_EOL;
Util\Timing::printRunTime();
} catch (DeepExitException $e) {
echo $e->getMessage();
return $e->getCode();
}//end try
if ($this->reporter->totalFixed === 0) {
// Nothing was fixed by PHPCBF.
if ($this->reporter->totalFixable === 0) {
// Nothing found that could be fixed.
return 0;
} else {
// Something failed to fix.
return 2;
}
}
if ($this->reporter->totalFixable === 0) {
// PHPCBF fixed all fixable errors.
return 1;
}
// PHPCBF fixed some fixable errors, but others failed to fix.
return 2;
}//end runPHPCBF()
/**
* Exits if the minimum requirements of PHP_CodSniffer are not met.
*
* @return array
*/
public function checkRequirements()
{
// Check the PHP version.
if (PHP_VERSION_ID < 50400) {
$error = 'ERROR: PHP_CodeSniffer requires PHP version 5.4.0 or greater.'.PHP_EOL;
throw new DeepExitException($error, 3);
}
if (extension_loaded('tokenizer') === false) {
$error = 'ERROR: PHP_CodeSniffer requires the tokenizer extension to be enabled.'.PHP_EOL;
throw new DeepExitException($error, 3);
}
}//end checkRequirements()
/**
* Init the rulesets and other high-level settings.
*
* @return void
*/
public function init()
{
if (defined('PHP_CODESNIFFER_CBF') === false) {
define('PHP_CODESNIFFER_CBF', false);
}
// Ensure this option is enabled or else line endings will not always
// be detected properly for files created on a Mac with the /r line ending.
ini_set('auto_detect_line_endings', true);
// Check that the standards are valid.
foreach ($this->config->standards as $standard) {
if (Util\Standards::isInstalledStandard($standard) === false) {
// They didn't select a valid coding standard, so help them
// out by letting them know which standards are installed.
$error = 'ERROR: the "'.$standard.'" coding standard is not installed. ';
ob_start();
Util\Standards::printInstalledStandards();
$error .= ob_get_contents();
ob_end_clean();
throw new DeepExitException($error, 3);
}
}
// Saves passing the Config object into other objects that only need
// the verbostity flag for deubg output.
if (defined('PHP_CODESNIFFER_VERBOSITY') === false) {
define('PHP_CODESNIFFER_VERBOSITY', $this->config->verbosity);
}
// Create this class so it is autoloaded and sets up a bunch
// of PHP_CodeSniffer-specific token type constants.
$tokens = new Util\Tokens();
// Allow autoloading of custom files inside installed standards.
$installedStandards = Standards::getInstalledStandardDetails();
foreach ($installedStandards as $name => $details) {
Autoload::addSearchPath($details['path'], $details['namespace']);
}
// The ruleset contains all the information about how the files
// should be checked and/or fixed.
try {
$this->ruleset = new Ruleset($this->config);
} catch (RuntimeException $e) {
$error = 'ERROR: '.$e->getMessage().PHP_EOL.PHP_EOL;
$error .= $this->config->printShortUsage(true);
throw new DeepExitException($error, 3);
}
}//end init()
/**
* Performs the run.
*
* @return int The number of errors and warnings found.
*/
private function run()
{
// The class that manages all reporters for the run.
$this->reporter = new Reporter($this->config);
// Include bootstrap files.
foreach ($this->config->bootstrap as $bootstrap) {
include $bootstrap;
}
if ($this->config->stdin === true) {
$fileContents = $this->config->stdinContent;
if ($fileContents === null) {
$handle = fopen('php://stdin', 'r');
stream_set_blocking($handle, true);
$fileContents = stream_get_contents($handle);
fclose($handle);
}
$todo = new FileList($this->config, $this->ruleset);
$dummy = new DummyFile($fileContents, $this->ruleset, $this->config);
$todo->addFile($dummy->path, $dummy);
} else {
if (empty($this->config->files) === true) {
$error = 'ERROR: You must supply at least one file or directory to process.'.PHP_EOL.PHP_EOL;
$error .= $this->config->printShortUsage(true);
throw new DeepExitException($error, 3);
}
if (PHP_CODESNIFFER_VERBOSITY > 0) {
echo 'Creating file list... ';
}
$todo = new FileList($this->config, $this->ruleset);
if (PHP_CODESNIFFER_VERBOSITY > 0) {
$numFiles = count($todo);
echo "DONE ($numFiles files in queue)".PHP_EOL;
}
if ($this->config->cache === true) {
if (PHP_CODESNIFFER_VERBOSITY > 0) {
echo 'Loading cache... ';
}
Cache::load($this->ruleset, $this->config);
if (PHP_CODESNIFFER_VERBOSITY > 0) {
$size = Cache::getSize();
echo "DONE ($size files in cache)".PHP_EOL;
}
}
}//end if
// Turn all sniff errors into exceptions.
set_error_handler([$this, 'handleErrors']);
// If verbosity is too high, turn off parallelism so the
// debug output is clean.
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$this->config->parallel = 1;
}
// If the PCNTL extension isn't installed, we can't fork.
if (function_exists('pcntl_fork') === false) {
$this->config->parallel = 1;
}
$lastDir = '';
$numFiles = count($todo);
if ($this->config->parallel === 1) {
// Running normally.
$numProcessed = 0;
foreach ($todo as $path => $file) {
if ($file->ignored === false) {
$currDir = dirname($path);
if ($lastDir !== $currDir) {
if (PHP_CODESNIFFER_VERBOSITY > 0) {
echo 'Changing into directory '.Common::stripBasepath($currDir, $this->config->basepath).PHP_EOL;
}
$lastDir = $currDir;
}
$this->processFile($file);
} else if (PHP_CODESNIFFER_VERBOSITY > 0) {
echo 'Skipping '.basename($file->path).PHP_EOL;
}
$numProcessed++;
$this->printProgress($file, $numFiles, $numProcessed);
}
} else {
// Batching and forking.
$childProcs = [];
$numPerBatch = ceil($numFiles / $this->config->parallel);
for ($batch = 0; $batch < $this->config->parallel; $batch++) {
$startAt = ($batch * $numPerBatch);
if ($startAt >= $numFiles) {
break;
}
$endAt = ($startAt + $numPerBatch);
if ($endAt > $numFiles) {
$endAt = $numFiles;
}
$childOutFilename = tempnam(sys_get_temp_dir(), 'phpcs-child');
$pid = pcntl_fork();
if ($pid === -1) {
throw new RuntimeException('Failed to create child process');
} else if ($pid !== 0) {
$childProcs[] = [
'pid' => $pid,
'out' => $childOutFilename,
];
} else {
// Move forward to the start of the batch.
$todo->rewind();
for ($i = 0; $i < $startAt; $i++) {
$todo->next();
}
// Reset the reporter to make sure only figures from this
// file batch are recorded.
$this->reporter->totalFiles = 0;
$this->reporter->totalErrors = 0;
$this->reporter->totalWarnings = 0;
$this->reporter->totalFixable = 0;
$this->reporter->totalFixed = 0;
// Process the files.
$pathsProcessed = [];
ob_start();
for ($i = $startAt; $i < $endAt; $i++) {
$path = $todo->key();
$file = $todo->current();
if ($file->ignored === true) {
continue;
}
$currDir = dirname($path);
if ($lastDir !== $currDir) {
if (PHP_CODESNIFFER_VERBOSITY > 0) {
echo 'Changing into directory '.Common::stripBasepath($currDir, $this->config->basepath).PHP_EOL;
}
$lastDir = $currDir;
}
$this->processFile($file);
$pathsProcessed[] = $path;
$todo->next();
}//end for
$debugOutput = ob_get_contents();
ob_end_clean();
// Write information about the run to the filesystem
// so it can be picked up by the main process.
$childOutput = [
'totalFiles' => $this->reporter->totalFiles,
'totalErrors' => $this->reporter->totalErrors,
'totalWarnings' => $this->reporter->totalWarnings,
'totalFixable' => $this->reporter->totalFixable,
'totalFixed' => $this->reporter->totalFixed,
];
$output = '<'.'?php'."\n".' $childOutput = ';
$output .= var_export($childOutput, true);
$output .= ";\n\$debugOutput = ";
$output .= var_export($debugOutput, true);
if ($this->config->cache === true) {
$childCache = [];
foreach ($pathsProcessed as $path) {
$childCache[$path] = Cache::get($path);
}
$output .= ";\n\$childCache = ";
$output .= var_export($childCache, true);
}
$output .= ";\n?".'>';
file_put_contents($childOutFilename, $output);
exit($pid);
}//end if
}//end for
$this->processChildProcs($childProcs);
}//end if
restore_error_handler();
if (PHP_CODESNIFFER_VERBOSITY === 0
&& $this->config->interactive === false
&& $this->config->showProgress === true
) {
echo PHP_EOL.PHP_EOL;
}
if ($this->config->cache === true) {
Cache::save();
}
$ignoreWarnings = Config::getConfigData('ignore_warnings_on_exit');
$ignoreErrors = Config::getConfigData('ignore_errors_on_exit');
$return = ($this->reporter->totalErrors + $this->reporter->totalWarnings);
if ($ignoreErrors !== null) {
$ignoreErrors = (bool) $ignoreErrors;
if ($ignoreErrors === true) {
$return -= $this->reporter->totalErrors;
}
}
if ($ignoreWarnings !== null) {
$ignoreWarnings = (bool) $ignoreWarnings;
if ($ignoreWarnings === true) {
$return -= $this->reporter->totalWarnings;
}
}
return $return;
}//end run()
/**
* Converts all PHP errors into exceptions.
*
* This method forces a sniff to stop processing if it is not
* able to handle a specific piece of code, instead of continuing
* and potentially getting into a loop.
*
* @param int $code The level of error raised.
* @param string $message The error message.
* @param string $file The path of the file that raised the error.
* @param int $line The line number the error was raised at.
*
* @return void
*/
public function handleErrors($code, $message, $file, $line)
{
if ((error_reporting() & $code) === 0) {
// This type of error is being muted.
return true;
}
throw new RuntimeException("$message in $file on line $line");
}//end handleErrors()
/**
* Processes a single file, including checking and fixing.
*
* @param \PHP_CodeSniffer\Files\File $file The file to be processed.
*
* @return void
*/
public function processFile($file)
{
if (PHP_CODESNIFFER_VERBOSITY > 0) {
$startTime = microtime(true);
echo 'Processing '.basename($file->path).' ';
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo PHP_EOL;
}
}
try {
$file->process();
if (PHP_CODESNIFFER_VERBOSITY > 0) {
$timeTaken = ((microtime(true) - $startTime) * 1000);
if ($timeTaken < 1000) {
$timeTaken = round($timeTaken);
echo "DONE in {$timeTaken}ms";
} else {
$timeTaken = round(($timeTaken / 1000), 2);
echo "DONE in $timeTaken secs";
}
if (PHP_CODESNIFFER_CBF === true) {
$errors = $file->getFixableCount();
echo " ($errors fixable violations)".PHP_EOL;
} else {
$errors = $file->getErrorCount();
$warnings = $file->getWarningCount();
echo " ($errors errors, $warnings warnings)".PHP_EOL;
}
}
} catch (\Exception $e) {
$error = 'An error occurred during processing; checking has been aborted. The error message was: '.$e->getMessage();
$file->addErrorOnLine($error, 1, 'Internal.Exception');
}//end try
$this->reporter->cacheFileReport($file, $this->config);
if ($this->config->interactive === true) {
/*
Running interactively.
Print the error report for the current file and then wait for user input.
*/
// Get current violations and then clear the list to make sure
// we only print violations for a single file each time.
$numErrors = null;
while ($numErrors !== 0) {
$numErrors = ($file->getErrorCount() + $file->getWarningCount());
if ($numErrors === 0) {
continue;
}
$this->reporter->printReport('full');
echo '<ENTER> to recheck, [s] to skip or [q] to quit : ';
$input = fgets(STDIN);
$input = trim($input);
switch ($input) {
case 's':
break(2);
case 'q':
throw new DeepExitException('', 0);
default:
// Repopulate the sniffs because some of them save their state
// and only clear it when the file changes, but we are rechecking
// the same file.
$file->ruleset->populateTokenListeners();
$file->reloadContent();
$file->process();
$this->reporter->cacheFileReport($file, $this->config);
break;
}
}//end while
}//end if
// Clean up the file to save (a lot of) memory.
$file->cleanUp();
}//end processFile()
/**
* Waits for child processes to complete and cleans up after them.
*
* The reporting information returned by each child process is merged
* into the main reporter class.
*
* @param array $childProcs An array of child processes to wait for.
*
* @return void
*/
private function processChildProcs($childProcs)
{
$numProcessed = 0;
$totalBatches = count($childProcs);
while (count($childProcs) > 0) {
foreach ($childProcs as $key => $procData) {
$res = pcntl_waitpid($procData['pid'], $status, WNOHANG);
if ($res === $procData['pid']) {
if (file_exists($procData['out']) === true) {
include $procData['out'];
if (isset($childOutput) === true) {
$this->reporter->totalFiles += $childOutput['totalFiles'];
$this->reporter->totalErrors += $childOutput['totalErrors'];
$this->reporter->totalWarnings += $childOutput['totalWarnings'];
$this->reporter->totalFixable += $childOutput['totalFixable'];
$this->reporter->totalFixed += $childOutput['totalFixed'];
}
if (isset($debugOutput) === true) {
echo $debugOutput;
}
if (isset($childCache) === true) {
foreach ($childCache as $path => $cache) {
Cache::set($path, $cache);
}
}
unlink($procData['out']);
unset($childProcs[$key]);
$numProcessed++;
// Fake a processed file so we can print progress output for the batch.
$file = new DummyFile(null, $this->ruleset, $this->config);
$file->setErrorCounts(
$childOutput['totalErrors'],
$childOutput['totalWarnings'],
$childOutput['totalFixable'],
$childOutput['totalFixed']
);
$this->printProgress($file, $totalBatches, $numProcessed);
}//end if
}//end if
}//end foreach
}//end while
}//end processChildProcs()
/**
* Print progress information for a single processed file.
*
* @param File $file The file that was processed.
* @param int $numFiles The total number of files to process.
* @param int $numProcessed The number of files that have been processed,
* including this one.
*
* @return void
*/
function printProgress($file, $numFiles, $numProcessed)
{
if (PHP_CODESNIFFER_VERBOSITY > 0
|| $this->config->showProgress === false
) {
return;
}
// Show progress information.
if ($file->ignored === true) {
echo 'S';
} else {
$errors = $file->getErrorCount();
$warnings = $file->getWarningCount();
$fixable = $file->getFixableCount();
$fixed = $file->getFixedCount();
if (PHP_CODESNIFFER_CBF === true) {
// Files with fixed errors or warnings are F (green).
// Files with unfixable errors or warnings are E (red).
// Files with no errors or warnings are . (black).
if ($fixable > 0) {
if ($this->config->colors === true) {
echo "\033[31m";
}
echo 'E';
if ($this->config->colors === true) {
echo "\033[0m";
}
} else if ($fixed > 0) {
if ($this->config->colors === true) {
echo "\033[32m";
}
echo 'F';
if ($this->config->colors === true) {
echo "\033[0m";
}
} else {
echo '.';
}//end if
} else {
// Files with errors are E (red).
// Files with fixable errors are E (green).
// Files with warnings are W (yellow).
// Files with fixable warnings are W (green).
// Files with no errors or warnings are . (black).
if ($errors > 0) {
if ($this->config->colors === true) {
if ($fixable > 0) {
echo "\033[32m";
} else {
echo "\033[31m";
}
}
echo 'E';
if ($this->config->colors === true) {
echo "\033[0m";
}
} else if ($warnings > 0) {
if ($this->config->colors === true) {
if ($fixable > 0) {
echo "\033[32m";
} else {
echo "\033[33m";
}
}
echo 'W';
if ($this->config->colors === true) {
echo "\033[0m";
}
} else {
echo '.';
}//end if
}//end if
}//end if
$numPerLine = 60;
if ($numProcessed !== $numFiles && ($numProcessed % $numPerLine) !== 0) {
return;
}
$percent = round(($numProcessed / $numFiles) * 100);
$padding = (strlen($numFiles) - strlen($numProcessed));
if ($numProcessed === $numFiles && $numFiles > $numPerLine) {
$padding += ($numPerLine - ($numFiles - (floor($numFiles / $numPerLine) * $numPerLine)));
}
echo str_repeat(' ', $padding)." $numProcessed / $numFiles ($percent%)".PHP_EOL;
}//end printProgress()
}//end class

View File

@ -0,0 +1,236 @@
<?php
/**
* Processes single and mutli-line arrays.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Sniffs;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
abstract class AbstractArraySniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
final public function register()
{
return [
T_ARRAY,
T_OPEN_SHORT_ARRAY,
];
}//end register()
/**
* Processes this sniff, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
* @param int $stackPtr The position of the current token in
* the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['code'] === T_ARRAY) {
$phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'no');
$arrayStart = $tokens[$stackPtr]['parenthesis_opener'];
if (isset($tokens[$arrayStart]['parenthesis_closer']) === false) {
// Incomplete array.
return;
}
$arrayEnd = $tokens[$arrayStart]['parenthesis_closer'];
} else {
$phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'yes');
$arrayStart = $stackPtr;
$arrayEnd = $tokens[$stackPtr]['bracket_closer'];
}
$lastContent = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($arrayEnd - 1), null, true);
if ($tokens[$lastContent]['code'] === T_COMMA) {
// Last array item ends with a comma.
$phpcsFile->recordMetric($stackPtr, 'Array end comma', 'yes');
$lastArrayToken = $lastContent;
} else {
$phpcsFile->recordMetric($stackPtr, 'Array end comma', 'no');
$lastArrayToken = $arrayEnd;
}
if ($tokens[$stackPtr]['code'] === T_ARRAY) {
$lastToken = $tokens[$stackPtr]['parenthesis_opener'];
} else {
$lastToken = $stackPtr;
}
$keyUsed = false;
$indices = [];
for ($checkToken = ($stackPtr + 1); $checkToken <= $lastArrayToken; $checkToken++) {
// Skip bracketed statements, like function calls.
if ($tokens[$checkToken]['code'] === T_OPEN_PARENTHESIS
&& (isset($tokens[$checkToken]['parenthesis_owner']) === false
|| $tokens[$checkToken]['parenthesis_owner'] !== $stackPtr)
) {
$checkToken = $tokens[$checkToken]['parenthesis_closer'];
continue;
}
if ($tokens[$checkToken]['code'] === T_ARRAY
|| $tokens[$checkToken]['code'] === T_OPEN_SHORT_ARRAY
|| $tokens[$checkToken]['code'] === T_CLOSURE
) {
// Let subsequent calls of this test handle nested arrays.
if ($tokens[$lastToken]['code'] !== T_DOUBLE_ARROW) {
$indices[] = ['value_start' => $checkToken];
$lastToken = $checkToken;
}
if ($tokens[$checkToken]['code'] === T_ARRAY) {
$checkToken = $tokens[$tokens[$checkToken]['parenthesis_opener']]['parenthesis_closer'];
} else if ($tokens[$checkToken]['code'] === T_OPEN_SHORT_ARRAY) {
$checkToken = $tokens[$checkToken]['bracket_closer'];
} else {
// T_CLOSURE.
$checkToken = $tokens[$checkToken]['scope_closer'];
}
$checkToken = $phpcsFile->findNext(T_WHITESPACE, ($checkToken + 1), null, true);
if ($tokens[$checkToken]['code'] !== T_COMMA) {
$checkToken--;
} else {
$lastToken = $checkToken;
}
continue;
}//end if
if ($tokens[$checkToken]['code'] !== T_DOUBLE_ARROW
&& $tokens[$checkToken]['code'] !== T_COMMA
&& $checkToken !== $arrayEnd
) {
continue;
}
if ($tokens[$checkToken]['code'] === T_COMMA
|| $checkToken === $arrayEnd
) {
$stackPtrCount = 0;
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
$stackPtrCount = count($tokens[$stackPtr]['nested_parenthesis']);
}
$commaCount = 0;
if (isset($tokens[$checkToken]['nested_parenthesis']) === true) {
$commaCount = count($tokens[$checkToken]['nested_parenthesis']);
if ($tokens[$stackPtr]['code'] === T_ARRAY) {
// Remove parenthesis that are used to define the array.
$commaCount--;
}
}
if ($commaCount > $stackPtrCount) {
// This comma is inside more parenthesis than the ARRAY keyword,
// so it is actually a comma used to do things like
// separate arguments in a function call.
continue;
}
if ($keyUsed === false) {
$valueContent = $phpcsFile->findNext(
Tokens::$emptyTokens,
($lastToken + 1),
$checkToken,
true
);
$indices[] = ['value_start' => $valueContent];
}
$lastToken = $checkToken;
$keyUsed = false;
continue;
}//end if
if ($tokens[$checkToken]['code'] === T_DOUBLE_ARROW) {
$keyUsed = true;
// Find the start of index that uses this double arrow.
$indexEnd = $phpcsFile->findPrevious(T_WHITESPACE, ($checkToken - 1), $arrayStart, true);
$indexStart = $phpcsFile->findStartOfStatement($indexEnd);
// Find the value of this index.
$nextContent = $phpcsFile->findNext(
Tokens::$emptyTokens,
($checkToken + 1),
$arrayEnd,
true
);
$indices[] = [
'index_start' => $indexStart,
'index_end' => $indexEnd,
'arrow' => $checkToken,
'value_start' => $nextContent,
];
$lastToken = $checkToken;
}//end if
}//end for
if ($tokens[$arrayStart]['line'] === $tokens[$arrayEnd]['line']) {
$this->processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
} else {
$this->processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
}
}//end process()
/**
* Processes a single-line array definition.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param int $arrayStart The token that starts the array definition.
* @param int $arrayEnd The token that ends the array definition.
* @param array $indices An array of token positions for the array keys,
* double arrows, and values.
*
* @return void
*/
abstract protected function processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
/**
* Processes a multi-line array definition.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param int $arrayStart The token that starts the array definition.
* @param int $arrayEnd The token that ends the array definition.
* @param array $indices An array of token positions for the array keys,
* double arrows, and values.
*
* @return void
*/
abstract protected function processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
}//end class

View File

@ -0,0 +1,938 @@
<?php
/**
* Processes pattern strings and checks that the code conforms to the pattern.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Sniffs;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Util\Tokens;
use PHP_CodeSniffer\Tokenizers\PHP;
use PHP_CodeSniffer\Exceptions\RuntimeException;
abstract class AbstractPatternSniff implements Sniff
{
/**
* If true, comments will be ignored if they are found in the code.
*
* @var boolean
*/
public $ignoreComments = false;
/**
* The current file being checked.
*
* @var string
*/
protected $currFile = '';
/**
* The parsed patterns array.
*
* @var array
*/
private $parsedPatterns = [];
/**
* Tokens that this sniff wishes to process outside of the patterns.
*
* @var int[]
* @see registerSupplementary()
* @see processSupplementary()
*/
private $supplementaryTokens = [];
/**
* Positions in the stack where errors have occurred.
*
* @var array<int, bool>
*/
private $errorPos = [];
/**
* Constructs a AbstractPatternSniff.
*
* @param boolean $ignoreComments If true, comments will be ignored.
*/
public function __construct($ignoreComments=null)
{
// This is here for backwards compatibility.
if ($ignoreComments !== null) {
$this->ignoreComments = $ignoreComments;
}
$this->supplementaryTokens = $this->registerSupplementary();
}//end __construct()
/**
* Registers the tokens to listen to.
*
* Classes extending <i>AbstractPatternTest</i> should implement the
* <i>getPatterns()</i> method to register the patterns they wish to test.
*
* @return int[]
* @see process()
*/
final public function register()
{
$listenTypes = [];
$patterns = $this->getPatterns();
foreach ($patterns as $pattern) {
$parsedPattern = $this->parse($pattern);
// Find a token position in the pattern that we can use
// for a listener token.
$pos = $this->getListenerTokenPos($parsedPattern);
$tokenType = $parsedPattern[$pos]['token'];
$listenTypes[] = $tokenType;
$patternArray = [
'listen_pos' => $pos,
'pattern' => $parsedPattern,
'pattern_code' => $pattern,
];
if (isset($this->parsedPatterns[$tokenType]) === false) {
$this->parsedPatterns[$tokenType] = [];
}
$this->parsedPatterns[$tokenType][] = $patternArray;
}//end foreach
return array_unique(array_merge($listenTypes, $this->supplementaryTokens));
}//end register()
/**
* Returns the token types that the specified pattern is checking for.
*
* Returned array is in the format:
* <code>
* array(
* T_WHITESPACE => 0, // 0 is the position where the T_WHITESPACE token
* // should occur in the pattern.
* );
* </code>
*
* @param array $pattern The parsed pattern to find the acquire the token
* types from.
*
* @return array<int, int>
*/
private function getPatternTokenTypes($pattern)
{
$tokenTypes = [];
foreach ($pattern as $pos => $patternInfo) {
if ($patternInfo['type'] === 'token') {
if (isset($tokenTypes[$patternInfo['token']]) === false) {
$tokenTypes[$patternInfo['token']] = $pos;
}
}
}
return $tokenTypes;
}//end getPatternTokenTypes()
/**
* Returns the position in the pattern that this test should register as
* a listener for the pattern.
*
* @param array $pattern The pattern to acquire the listener for.
*
* @return int The position in the pattern that this test should register
* as the listener.
* @throws RuntimeException If we could not determine a token to listen for.
*/
private function getListenerTokenPos($pattern)
{
$tokenTypes = $this->getPatternTokenTypes($pattern);
$tokenCodes = array_keys($tokenTypes);
$token = Tokens::getHighestWeightedToken($tokenCodes);
// If we could not get a token.
if ($token === false) {
$error = 'Could not determine a token to listen for';
throw new RuntimeException($error);
}
return $tokenTypes[$token];
}//end getListenerTokenPos()
/**
* Processes the test.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token occurred.
* @param int $stackPtr The position in the tokens stack
* where the listening token type
* was found.
*
* @return void
* @see register()
*/
final public function process(File $phpcsFile, $stackPtr)
{
$file = $phpcsFile->getFilename();
if ($this->currFile !== $file) {
// We have changed files, so clean up.
$this->errorPos = [];
$this->currFile = $file;
}
$tokens = $phpcsFile->getTokens();
if (in_array($tokens[$stackPtr]['code'], $this->supplementaryTokens) === true) {
$this->processSupplementary($phpcsFile, $stackPtr);
}
$type = $tokens[$stackPtr]['code'];
// If the type is not set, then it must have been a token registered
// with registerSupplementary().
if (isset($this->parsedPatterns[$type]) === false) {
return;
}
$allErrors = [];
// Loop over each pattern that is listening to the current token type
// that we are processing.
foreach ($this->parsedPatterns[$type] as $patternInfo) {
// If processPattern returns false, then the pattern that we are
// checking the code with must not be designed to check that code.
$errors = $this->processPattern($patternInfo, $phpcsFile, $stackPtr);
if ($errors === false) {
// The pattern didn't match.
continue;
} else if (empty($errors) === true) {
// The pattern matched, but there were no errors.
break;
}
foreach ($errors as $stackPtr => $error) {
if (isset($this->errorPos[$stackPtr]) === false) {
$this->errorPos[$stackPtr] = true;
$allErrors[$stackPtr] = $error;
}
}
}
foreach ($allErrors as $stackPtr => $error) {
$phpcsFile->addError($error, $stackPtr, 'Found');
}
}//end process()
/**
* Processes the pattern and verifies the code at $stackPtr.
*
* @param array $patternInfo Information about the pattern used
* for checking, which includes are
* parsed token representation of the
* pattern.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token occurred.
* @param int $stackPtr The position in the tokens stack where
* the listening token type was found.
*
* @return array
*/
protected function processPattern($patternInfo, File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$pattern = $patternInfo['pattern'];
$patternCode = $patternInfo['pattern_code'];
$errors = [];
$found = '';
$ignoreTokens = [T_WHITESPACE];
if ($this->ignoreComments === true) {
$ignoreTokens
= array_merge($ignoreTokens, Tokens::$commentTokens);
}
$origStackPtr = $stackPtr;
$hasError = false;
if ($patternInfo['listen_pos'] > 0) {
$stackPtr--;
for ($i = ($patternInfo['listen_pos'] - 1); $i >= 0; $i--) {
if ($pattern[$i]['type'] === 'token') {
if ($pattern[$i]['token'] === T_WHITESPACE) {
if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
$found = $tokens[$stackPtr]['content'].$found;
}
// Only check the size of the whitespace if this is not
// the first token. We don't care about the size of
// leading whitespace, just that there is some.
if ($i !== 0) {
if ($tokens[$stackPtr]['content'] !== $pattern[$i]['value']) {
$hasError = true;
}
}
} else {
// Check to see if this important token is the same as the
// previous important token in the pattern. If it is not,
// then the pattern cannot be for this piece of code.
$prev = $phpcsFile->findPrevious(
$ignoreTokens,
$stackPtr,
null,
true
);
if ($prev === false
|| $tokens[$prev]['code'] !== $pattern[$i]['token']
) {
return false;
}
// If we skipped past some whitespace tokens, then add them
// to the found string.
$tokenContent = $phpcsFile->getTokensAsString(
($prev + 1),
($stackPtr - $prev - 1)
);
$found = $tokens[$prev]['content'].$tokenContent.$found;
if (isset($pattern[($i - 1)]) === true
&& $pattern[($i - 1)]['type'] === 'skip'
) {
$stackPtr = $prev;
} else {
$stackPtr = ($prev - 1);
}
}//end if
} else if ($pattern[$i]['type'] === 'skip') {
// Skip to next piece of relevant code.
if ($pattern[$i]['to'] === 'parenthesis_closer') {
$to = 'parenthesis_opener';
} else {
$to = 'scope_opener';
}
// Find the previous opener.
$next = $phpcsFile->findPrevious(
$ignoreTokens,
$stackPtr,
null,
true
);
if ($next === false || isset($tokens[$next][$to]) === false) {
// If there was not opener, then we must be
// using the wrong pattern.
return false;
}
if ($to === 'parenthesis_opener') {
$found = '{'.$found;
} else {
$found = '('.$found;
}
$found = '...'.$found;
// Skip to the opening token.
$stackPtr = ($tokens[$next][$to] - 1);
} else if ($pattern[$i]['type'] === 'string') {
$found = 'abc';
} else if ($pattern[$i]['type'] === 'newline') {
if ($this->ignoreComments === true
&& isset(Tokens::$commentTokens[$tokens[$stackPtr]['code']]) === true
) {
$startComment = $phpcsFile->findPrevious(
Tokens::$commentTokens,
($stackPtr - 1),
null,
true
);
if ($tokens[$startComment]['line'] !== $tokens[($startComment + 1)]['line']) {
$startComment++;
}
$tokenContent = $phpcsFile->getTokensAsString(
$startComment,
($stackPtr - $startComment + 1)
);
$found = $tokenContent.$found;
$stackPtr = ($startComment - 1);
}
if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
if ($tokens[$stackPtr]['content'] !== $phpcsFile->eolChar) {
$found = $tokens[$stackPtr]['content'].$found;
// This may just be an indent that comes after a newline
// so check the token before to make sure. If it is a newline, we
// can ignore the error here.
if (($tokens[($stackPtr - 1)]['content'] !== $phpcsFile->eolChar)
&& ($this->ignoreComments === true
&& isset(Tokens::$commentTokens[$tokens[($stackPtr - 1)]['code']]) === false)
) {
$hasError = true;
} else {
$stackPtr--;
}
} else {
$found = 'EOL'.$found;
}
} else {
$found = $tokens[$stackPtr]['content'].$found;
$hasError = true;
}//end if
if ($hasError === false && $pattern[($i - 1)]['type'] !== 'newline') {
// Make sure they only have 1 newline.
$prev = $phpcsFile->findPrevious($ignoreTokens, ($stackPtr - 1), null, true);
if ($prev !== false && $tokens[$prev]['line'] !== $tokens[$stackPtr]['line']) {
$hasError = true;
}
}
}//end if
}//end for
}//end if
$stackPtr = $origStackPtr;
$lastAddedStackPtr = null;
$patternLen = count($pattern);
for ($i = $patternInfo['listen_pos']; $i < $patternLen; $i++) {
if (isset($tokens[$stackPtr]) === false) {
break;
}
if ($pattern[$i]['type'] === 'token') {
if ($pattern[$i]['token'] === T_WHITESPACE) {
if ($this->ignoreComments === true) {
// If we are ignoring comments, check to see if this current
// token is a comment. If so skip it.
if (isset(Tokens::$commentTokens[$tokens[$stackPtr]['code']]) === true) {
continue;
}
// If the next token is a comment, the we need to skip the
// current token as we should allow a space before a
// comment for readability.
if (isset($tokens[($stackPtr + 1)]) === true
&& isset(Tokens::$commentTokens[$tokens[($stackPtr + 1)]['code']]) === true
) {
continue;
}
}
$tokenContent = '';
if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
if (isset($pattern[($i + 1)]) === false) {
// This is the last token in the pattern, so just compare
// the next token of content.
$tokenContent = $tokens[$stackPtr]['content'];
} else {
// Get all the whitespace to the next token.
$next = $phpcsFile->findNext(
Tokens::$emptyTokens,
$stackPtr,
null,
true
);
$tokenContent = $phpcsFile->getTokensAsString(
$stackPtr,
($next - $stackPtr)
);
$lastAddedStackPtr = $stackPtr;
$stackPtr = $next;
}//end if
if ($stackPtr !== $lastAddedStackPtr) {
$found .= $tokenContent;
}
} else {
if ($stackPtr !== $lastAddedStackPtr) {
$found .= $tokens[$stackPtr]['content'];
$lastAddedStackPtr = $stackPtr;
}
}//end if
if (isset($pattern[($i + 1)]) === true
&& $pattern[($i + 1)]['type'] === 'skip'
) {
// The next token is a skip token, so we just need to make
// sure the whitespace we found has *at least* the
// whitespace required.
if (strpos($tokenContent, $pattern[$i]['value']) !== 0) {
$hasError = true;
}
} else {
if ($tokenContent !== $pattern[$i]['value']) {
$hasError = true;
}
}
} else {
// Check to see if this important token is the same as the
// next important token in the pattern. If it is not, then
// the pattern cannot be for this piece of code.
$next = $phpcsFile->findNext(
$ignoreTokens,
$stackPtr,
null,
true
);
if ($next === false
|| $tokens[$next]['code'] !== $pattern[$i]['token']
) {
// The next important token did not match the pattern.
return false;
}
if ($lastAddedStackPtr !== null) {
if (($tokens[$next]['code'] === T_OPEN_CURLY_BRACKET
|| $tokens[$next]['code'] === T_CLOSE_CURLY_BRACKET)
&& isset($tokens[$next]['scope_condition']) === true
&& $tokens[$next]['scope_condition'] > $lastAddedStackPtr
) {
// This is a brace, but the owner of it is after the current
// token, which means it does not belong to any token in
// our pattern. This means the pattern is not for us.
return false;
}
if (($tokens[$next]['code'] === T_OPEN_PARENTHESIS
|| $tokens[$next]['code'] === T_CLOSE_PARENTHESIS)
&& isset($tokens[$next]['parenthesis_owner']) === true
&& $tokens[$next]['parenthesis_owner'] > $lastAddedStackPtr
) {
// This is a bracket, but the owner of it is after the current
// token, which means it does not belong to any token in
// our pattern. This means the pattern is not for us.
return false;
}
}//end if
// If we skipped past some whitespace tokens, then add them
// to the found string.
if (($next - $stackPtr) > 0) {
$hasComment = false;
for ($j = $stackPtr; $j < $next; $j++) {
$found .= $tokens[$j]['content'];
if (isset(Tokens::$commentTokens[$tokens[$j]['code']]) === true) {
$hasComment = true;
}
}
// If we are not ignoring comments, this additional
// whitespace or comment is not allowed. If we are
// ignoring comments, there needs to be at least one
// comment for this to be allowed.
if ($this->ignoreComments === false
|| ($this->ignoreComments === true
&& $hasComment === false)
) {
$hasError = true;
}
// Even when ignoring comments, we are not allowed to include
// newlines without the pattern specifying them, so
// everything should be on the same line.
if ($tokens[$next]['line'] !== $tokens[$stackPtr]['line']) {
$hasError = true;
}
}//end if
if ($next !== $lastAddedStackPtr) {
$found .= $tokens[$next]['content'];
$lastAddedStackPtr = $next;
}
if (isset($pattern[($i + 1)]) === true
&& $pattern[($i + 1)]['type'] === 'skip'
) {
$stackPtr = $next;
} else {
$stackPtr = ($next + 1);
}
}//end if
} else if ($pattern[$i]['type'] === 'skip') {
if ($pattern[$i]['to'] === 'unknown') {
$next = $phpcsFile->findNext(
$pattern[($i + 1)]['token'],
$stackPtr
);
if ($next === false) {
// Couldn't find the next token, so we must
// be using the wrong pattern.
return false;
}
$found .= '...';
$stackPtr = $next;
} else {
// Find the previous opener.
$next = $phpcsFile->findPrevious(
Tokens::$blockOpeners,
$stackPtr
);
if ($next === false
|| isset($tokens[$next][$pattern[$i]['to']]) === false
) {
// If there was not opener, then we must
// be using the wrong pattern.
return false;
}
$found .= '...';
if ($pattern[$i]['to'] === 'parenthesis_closer') {
$found .= ')';
} else {
$found .= '}';
}
// Skip to the closing token.
$stackPtr = ($tokens[$next][$pattern[$i]['to']] + 1);
}//end if
} else if ($pattern[$i]['type'] === 'string') {
if ($tokens[$stackPtr]['code'] !== T_STRING) {
$hasError = true;
}
if ($stackPtr !== $lastAddedStackPtr) {
$found .= 'abc';
$lastAddedStackPtr = $stackPtr;
}
$stackPtr++;
} else if ($pattern[$i]['type'] === 'newline') {
// Find the next token that contains a newline character.
$newline = 0;
for ($j = $stackPtr; $j < $phpcsFile->numTokens; $j++) {
if (strpos($tokens[$j]['content'], $phpcsFile->eolChar) !== false) {
$newline = $j;
break;
}
}
if ($newline === 0) {
// We didn't find a newline character in the rest of the file.
$next = ($phpcsFile->numTokens - 1);
$hasError = true;
} else {
if ($this->ignoreComments === false) {
// The newline character cannot be part of a comment.
if (isset(Tokens::$commentTokens[$tokens[$newline]['code']]) === true) {
$hasError = true;
}
}
if ($newline === $stackPtr) {
$next = ($stackPtr + 1);
} else {
// Check that there were no significant tokens that we
// skipped over to find our newline character.
$next = $phpcsFile->findNext(
$ignoreTokens,
$stackPtr,
null,
true
);
if ($next < $newline) {
// We skipped a non-ignored token.
$hasError = true;
} else {
$next = ($newline + 1);
}
}
}//end if
if ($stackPtr !== $lastAddedStackPtr) {
$found .= $phpcsFile->getTokensAsString(
$stackPtr,
($next - $stackPtr)
);
$lastAddedStackPtr = ($next - 1);
}
$stackPtr = $next;
}//end if
}//end for
if ($hasError === true) {
$error = $this->prepareError($found, $patternCode);
$errors[$origStackPtr] = $error;
}
return $errors;
}//end processPattern()
/**
* Prepares an error for the specified patternCode.
*
* @param string $found The actual found string in the code.
* @param string $patternCode The expected pattern code.
*
* @return string The error message.
*/
protected function prepareError($found, $patternCode)
{
$found = str_replace("\r\n", '\n', $found);
$found = str_replace("\n", '\n', $found);
$found = str_replace("\r", '\n', $found);
$found = str_replace("\t", '\t', $found);
$found = str_replace('EOL', '\n', $found);
$expected = str_replace('EOL', '\n', $patternCode);
$error = "Expected \"$expected\"; found \"$found\"";
return $error;
}//end prepareError()
/**
* Returns the patterns that should be checked.
*
* @return string[]
*/
abstract protected function getPatterns();
/**
* Registers any supplementary tokens that this test might wish to process.
*
* A sniff may wish to register supplementary tests when it wishes to group
* an arbitrary validation that cannot be performed using a pattern, with
* other pattern tests.
*
* @return int[]
* @see processSupplementary()
*/
protected function registerSupplementary()
{
return [];
}//end registerSupplementary()
/**
* Processes any tokens registered with registerSupplementary().
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where to
* process the skip.
* @param int $stackPtr The position in the tokens stack to
* process.
*
* @return void
* @see registerSupplementary()
*/
protected function processSupplementary(File $phpcsFile, $stackPtr)
{
}//end processSupplementary()
/**
* Parses a pattern string into an array of pattern steps.
*
* @param string $pattern The pattern to parse.
*
* @return array The parsed pattern array.
* @see createSkipPattern()
* @see createTokenPattern()
*/
private function parse($pattern)
{
$patterns = [];
$length = strlen($pattern);
$lastToken = 0;
$firstToken = 0;
for ($i = 0; $i < $length; $i++) {
$specialPattern = false;
$isLastChar = ($i === ($length - 1));
$oldFirstToken = $firstToken;
if (substr($pattern, $i, 3) === '...') {
// It's a skip pattern. The skip pattern requires the
// content of the token in the "from" position and the token
// to skip to.
$specialPattern = $this->createSkipPattern($pattern, ($i - 1));
$lastToken = ($i - $firstToken);
$firstToken = ($i + 3);
$i = ($i + 2);
if ($specialPattern['to'] !== 'unknown') {
$firstToken++;
}
} else if (substr($pattern, $i, 3) === 'abc') {
$specialPattern = ['type' => 'string'];
$lastToken = ($i - $firstToken);
$firstToken = ($i + 3);
$i = ($i + 2);
} else if (substr($pattern, $i, 3) === 'EOL') {
$specialPattern = ['type' => 'newline'];
$lastToken = ($i - $firstToken);
$firstToken = ($i + 3);
$i = ($i + 2);
}//end if
if ($specialPattern !== false || $isLastChar === true) {
// If we are at the end of the string, don't worry about a limit.
if ($isLastChar === true) {
// Get the string from the end of the last skip pattern, if any,
// to the end of the pattern string.
$str = substr($pattern, $oldFirstToken);
} else {
// Get the string from the end of the last special pattern,
// if any, to the start of this special pattern.
if ($lastToken === 0) {
// Note that if the last special token was zero characters ago,
// there will be nothing to process so we can skip this bit.
// This happens if you have something like: EOL... in your pattern.
$str = '';
} else {
$str = substr($pattern, $oldFirstToken, $lastToken);
}
}
if ($str !== '') {
$tokenPatterns = $this->createTokenPattern($str);
foreach ($tokenPatterns as $tokenPattern) {
$patterns[] = $tokenPattern;
}
}
// Make sure we don't skip the last token.
if ($isLastChar === false && $i === ($length - 1)) {
$i--;
}
}//end if
// Add the skip pattern *after* we have processed
// all the tokens from the end of the last skip pattern
// to the start of this skip pattern.
if ($specialPattern !== false) {
$patterns[] = $specialPattern;
}
}//end for
return $patterns;
}//end parse()
/**
* Creates a skip pattern.
*
* @param string $pattern The pattern being parsed.
* @param string $from The token content that the skip pattern starts from.
*
* @return array The pattern step.
* @see createTokenPattern()
* @see parse()
*/
private function createSkipPattern($pattern, $from)
{
$skip = ['type' => 'skip'];
$nestedParenthesis = 0;
$nestedBraces = 0;
for ($start = $from; $start >= 0; $start--) {
switch ($pattern[$start]) {
case '(':
if ($nestedParenthesis === 0) {
$skip['to'] = 'parenthesis_closer';
}
$nestedParenthesis--;
break;
case '{':
if ($nestedBraces === 0) {
$skip['to'] = 'scope_closer';
}
$nestedBraces--;
break;
case '}':
$nestedBraces++;
break;
case ')':
$nestedParenthesis++;
break;
}//end switch
if (isset($skip['to']) === true) {
break;
}
}//end for
if (isset($skip['to']) === false) {
$skip['to'] = 'unknown';
}
return $skip;
}//end createSkipPattern()
/**
* Creates a token pattern.
*
* @param string $str The tokens string that the pattern should match.
*
* @return array The pattern step.
* @see createSkipPattern()
* @see parse()
*/
private function createTokenPattern($str)
{
// Don't add a space after the closing php tag as it will add a new
// whitespace token.
$tokenizer = new PHP('<?php '.$str.'?>', null);
// Remove the <?php tag from the front and the end php tag from the back.
$tokens = $tokenizer->getTokens();
$tokens = array_slice($tokens, 1, (count($tokens) - 2));
$patterns = [];
foreach ($tokens as $patternInfo) {
$patterns[] = [
'type' => 'token',
'token' => $patternInfo['code'],
'value' => $patternInfo['content'],
];
}
return $patterns;
}//end createTokenPattern()
}//end class

View File

@ -0,0 +1,175 @@
<?php
/**
* Allows tests that extend this class to listen for tokens within a particular scope.
*
* Below is a test that listens to methods that exist only within classes:
* <code>
* class ClassScopeTest extends PHP_CodeSniffer_Standards_AbstractScopeSniff
* {
* public function __construct()
* {
* parent::__construct(array(T_CLASS), array(T_FUNCTION));
* }
*
* protected function processTokenWithinScope(\PHP_CodeSniffer\Files\File $phpcsFile, $)
* {
* $className = $phpcsFile->getDeclarationName($currScope);
* echo 'encountered a method within class '.$className;
* }
* }
* </code>
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Sniffs;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Exceptions\RuntimeException;
abstract class AbstractScopeSniff implements Sniff
{
/**
* The token types that this test wishes to listen to within the scope.
*
* @var array
*/
private $tokens = [];
/**
* The type of scope opener tokens that this test wishes to listen to.
*
* @var string
*/
private $scopeTokens = [];
/**
* True if this test should fire on tokens outside of the scope.
*
* @var boolean
*/
private $listenOutside = false;
/**
* Constructs a new AbstractScopeTest.
*
* @param array $scopeTokens The type of scope the test wishes to listen to.
* @param array $tokens The tokens that the test wishes to listen to
* within the scope.
* @param boolean $listenOutside If true this test will also alert the
* extending class when a token is found outside
* the scope, by calling the
* processTokenOutsideScope method.
*
* @see PHP_CodeSniffer.getValidScopeTokeners()
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified tokens array is empty.
*/
public function __construct(
array $scopeTokens,
array $tokens,
$listenOutside=false
) {
if (empty($scopeTokens) === true) {
$error = 'The scope tokens list cannot be empty';
throw new RuntimeException($error);
}
if (empty($tokens) === true) {
$error = 'The tokens list cannot be empty';
throw new RuntimeException($error);
}
$invalidScopeTokens = array_intersect($scopeTokens, $tokens);
if (empty($invalidScopeTokens) === false) {
$invalid = implode(', ', $invalidScopeTokens);
$error = "Scope tokens [$invalid] can't be in the tokens array";
throw new RuntimeException($error);
}
$this->listenOutside = $listenOutside;
$this->scopeTokens = array_flip($scopeTokens);
$this->tokens = $tokens;
}//end __construct()
/**
* The method that is called to register the tokens this test wishes to
* listen to.
*
* DO NOT OVERRIDE THIS METHOD. Use the constructor of this class to register
* for the desired tokens and scope.
*
* @return int[]
* @see __constructor()
*/
final public function register()
{
return $this->tokens;
}//end register()
/**
* Processes the tokens that this test is listening for.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param int $stackPtr The position in the stack where this
* token was found.
*
* @return void
* @see processTokenWithinScope()
*/
final public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$foundScope = false;
foreach ($tokens[$stackPtr]['conditions'] as $scope => $code) {
if (isset($this->scopeTokens[$code]) === true) {
$this->processTokenWithinScope($phpcsFile, $stackPtr, $scope);
$foundScope = true;
}
}
if ($this->listenOutside === true && $foundScope === false) {
$this->processTokenOutsideScope($phpcsFile, $stackPtr);
}
}//end process()
/**
* Processes a token that is found within the scope that this test is
* listening to.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param int $stackPtr The position in the stack where this
* token was found.
* @param int $currScope The position in the tokens array that
* opened the scope that this test is
* listening for.
*
* @return void
*/
abstract protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope);
/**
* Processes a token that is found outside the scope that this test is
* listening to.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param int $stackPtr The position in the stack where this
* token was found.
*
* @return void
*/
abstract protected function processTokenOutsideScope(File $phpcsFile, $stackPtr);
}//end class

View File

@ -0,0 +1,194 @@
<?php
/**
* A class to find T_VARIABLE tokens.
*
* This class can distinguish between normal T_VARIABLE tokens, and those tokens
* that represent class members. If a class member is encountered, then the
* processMemberVar method is called so the extending class can process it. If
* the token is found to be a normal T_VARIABLE token, then processVariable is
* called.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Sniffs;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
use PHP_CodeSniffer\Exceptions\RuntimeException;
abstract class AbstractVariableSniff extends AbstractScopeSniff
{
/**
* Constructs an AbstractVariableTest.
*/
public function __construct()
{
$scopes = Tokens::$ooScopeTokens;
$listen = [
T_VARIABLE,
T_DOUBLE_QUOTED_STRING,
T_HEREDOC,
];
parent::__construct($scopes, $listen, true);
}//end __construct()
/**
* Processes the token in the specified PHP_CodeSniffer_File.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
* token was found.
* @param int $stackPtr The position where the token was found.
* @param int $currScope The current scope opener token.
*
* @return void
*/
final protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['code'] === T_DOUBLE_QUOTED_STRING
|| $tokens[$stackPtr]['code'] === T_HEREDOC
) {
// Check to see if this string has a variable in it.
$pattern = '|(?<!\\\\)(?:\\\\{2})*\${?[a-zA-Z0-9_]+}?|';
if (preg_match($pattern, $tokens[$stackPtr]['content']) !== 0) {
$this->processVariableInString($phpcsFile, $stackPtr);
}
return;
}
// If this token is inside nested inside a function at a deeper
// level than the current OO scope that was found, it's a normal
// variable and not a member var.
$conditions = array_reverse($tokens[$stackPtr]['conditions'], true);
$inFunction = false;
foreach ($conditions as $scope => $code) {
if (isset(Tokens::$ooScopeTokens[$code]) === true) {
break;
}
if ($code === T_FUNCTION || $code === T_CLOSURE) {
$inFunction = true;
}
}
if ($scope !== $currScope) {
// We found a closer scope to this token, so ignore
// this particular time through the sniff. We will process
// this token when this closer scope is found to avoid
// duplicate checks.
return;
}
// Just make sure this isn't a variable in a function declaration.
if ($inFunction === false && isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
foreach ($tokens[$stackPtr]['nested_parenthesis'] as $opener => $closer) {
if (isset($tokens[$opener]['parenthesis_owner']) === false) {
// Check if this is a USE statement in a closure.
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), null, true);
if ($tokens[$prev]['code'] === T_USE) {
$inFunction = true;
break;
}
continue;
}
$owner = $tokens[$opener]['parenthesis_owner'];
if ($tokens[$owner]['code'] === T_FUNCTION
|| $tokens[$owner]['code'] === T_CLOSURE
) {
$inFunction = true;
break;
}
}
}//end if
if ($inFunction === true) {
$this->processVariable($phpcsFile, $stackPtr);
} else {
$this->processMemberVar($phpcsFile, $stackPtr);
}
}//end processTokenWithinScope()
/**
* Processes the token outside the scope in the file.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
* token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void
*/
final protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// These variables are not member vars.
if ($tokens[$stackPtr]['code'] === T_VARIABLE) {
$this->processVariable($phpcsFile, $stackPtr);
} else if ($tokens[$stackPtr]['code'] === T_DOUBLE_QUOTED_STRING
|| $tokens[$stackPtr]['code'] === T_HEREDOC
) {
// Check to see if this string has a variable in it.
$pattern = '|(?<!\\\\)(?:\\\\{2})*\${?[a-zA-Z0-9_]+}?|';
if (preg_match($pattern, $tokens[$stackPtr]['content']) !== 0) {
$this->processVariableInString($phpcsFile, $stackPtr);
}
}
}//end processTokenOutsideScope()
/**
* Called to process class member vars.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
* token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void
*/
abstract protected function processMemberVar(File $phpcsFile, $stackPtr);
/**
* Called to process normal member vars.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
* token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void
*/
abstract protected function processVariable(File $phpcsFile, $stackPtr);
/**
* Called to process variables found in double quoted strings or heredocs.
*
* Note that there may be more than one variable in the string, which will
* result only in one call for the string or one call per line for heredocs.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
* token was found.
* @param int $stackPtr The position where the double quoted
* string was found.
*
* @return void
*/
abstract protected function processVariableInString(File $phpcsFile, $stackPtr);
}//end class

View File

@ -0,0 +1,80 @@
<?php
/**
* Represents a PHP_CodeSniffer sniff for sniffing coding standards.
*
* A sniff registers what token types it wishes to listen for, then, when
* PHP_CodeSniffer encounters that token, the sniff is invoked and passed
* information about where the token was found in the stack, and the
* PHP_CodeSniffer file in which the token was found.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Sniffs;
use PHP_CodeSniffer\Files\File;
interface Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* An example return value for a sniff that wants to listen for whitespace
* and any comments would be:
*
* <code>
* return array(
* T_WHITESPACE,
* T_DOC_COMMENT,
* T_COMMENT,
* );
* </code>
*
* @return int[]
* @see Tokens.php
*/
public function register();
/**
* Called when one of the token types that this sniff is listening for
* is found.
*
* The stackPtr variable indicates where in the stack the token was found.
* A sniff can acquire information this token, along with all the other
* tokens within the stack by first acquiring the token stack:
*
* <code>
* $tokens = $phpcsFile->getTokens();
* echo 'Encountered a '.$tokens[$stackPtr]['type'].' token';
* echo 'token information: ';
* print_r($tokens[$stackPtr]);
* </code>
*
* If the sniff discovers an anomaly in the code, they can raise an error
* by calling addError() on the \PHP_CodeSniffer\Files\File object, specifying an error
* message and the position of the offending token:
*
* <code>
* $phpcsFile->addError('Encountered an error', $stackPtr);
* </code>
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
* @param int $stackPtr The position in the PHP_CodeSniffer
* file's token stack where the token
* was found.
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return (count($tokens) + 1) to skip
* the rest of the file.
*/
public function process(File $phpcsFile, $stackPtr);
}//end interface

View File

@ -0,0 +1,23 @@
<documentation title="Short Array Syntax">
<standard>
<![CDATA[
Short array syntax must be used to define arrays.
]]>
</standard>
<code_comparison>
<code title="Valid: Short form of array.">
<![CDATA[
$arr = <em>[</em>
'foo' => 'bar',
<em>]</em>;
]]>
</code>
<code title="Invalid: Long form of array.">
<![CDATA[
$arr = <em>array(</em>
'foo' => 'bar',
<em>)</em>;
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,23 @@
<documentation title="Long Array Syntax">
<standard>
<![CDATA[
Long array syntax must be used to define arrays.
]]>
</standard>
<code_comparison>
<code title="Valid: Long form of array.">
<![CDATA[
$arr = <em>array(</em>
'foo' => 'bar',
<em>)</em>;
]]>
</code>
<code title="Invalid: Short form of array.">
<![CDATA[
$arr = <em>[</em>
'foo' => 'bar',
<em>]</em>;
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,27 @@
<documentation title="Duplicate Class Names">
<standard>
<![CDATA[
Class and Interface names should be unique in a project. They should never be duplicated.
]]>
</standard>
<code_comparison>
<code title="Valid: A unique class name.">
<![CDATA[
class <em>Foo</em>
{
}
]]>
</code>
<code title="Invalid: A class duplicated (including across multiple files).">
<![CDATA[
class <em>Foo</em>
{
}
class <em>Foo</em>
{
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,28 @@
<documentation title="Opening Brace on Same Line">
<standard>
<![CDATA[
The opening brace of a class must be on the same line after the definition and must be the last thing on that line.
]]>
</standard>
<code_comparison>
<code title="Valid: Opening brace on the same line.">
<![CDATA[
class Foo <em>{</em>
}
]]>
</code>
<code title="Invalid: Opening brace on the next line.">
<![CDATA[
class Foo
<em>{</em>
}
]]>
</code>
<code title="Invalid: Opening brace not last thing on the line.">
<![CDATA[
class Foo {<em> // Start of class.</em>
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,23 @@
<documentation title="Assignment In Condition">
<standard>
<![CDATA[
Variable assignments should not be made within conditions.
]]>
</standard>
<code_comparison>
<code title="Valid: A variable comparison being executed within a condition.">
<![CDATA[
if (<em>$test === 'abc'</em>) {
// Code.
}
]]>
</code>
<code title="Invalid: A variable assignment being made within a condition.">
<![CDATA[
if (<em>$test = 'abc'</em>) {
// Code.
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,23 @@
<documentation title="Empty Statements">
<standard>
<![CDATA[
Control Structures must have at least one statement inside of the body.
]]>
</standard>
<code_comparison>
<code title="Valid: There is a statement inside the control structure.">
<![CDATA[
if ($test) {
$var = 1;
}
]]>
</code>
<code title="Invalid: The control structure has no statements.">
<![CDATA[
if ($test) {
<em>// do nothing</em>
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,23 @@
<documentation title="Condition-Only For Loops">
<standard>
<![CDATA[
For loops that have only a second expression (the condition) should be converted to while loops.
]]>
</standard>
<code_comparison>
<code title="Valid: A for loop is used with all three expressions.">
<![CDATA[
for (<em>$i = 0</em>; $i < 10; <em>$i++</em>) {
echo "{$i}\n";
}
]]>
</code>
<code title="Invalid: A for loop is used without a first or third expression.">
<![CDATA[
for (<em></em>;$test;<em></em>) {
$test = doSomething();
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,24 @@
<documentation title="For Loops With Function Calls in the Test">
<standard>
<![CDATA[
For loops should not call functions inside the test for the loop when they can be computed beforehand.
]]>
</standard>
<code_comparison>
<code title="Valid: A for loop that determines its end condition before the loop starts.">
<![CDATA[
<em>$end = count($foo);</em>
for ($i = 0; $i < $end; $i++) {
echo $foo[$i]."\n";
}
]]>
</code>
<code title="Invalid: A for loop that unnecessarily computes the same value on every iteration.">
<![CDATA[
for ($i = 0; $i < <em>count($foo)</em>; $i++) {
echo $foo[$i]."\n";
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,25 @@
<documentation title="Jumbled Incrementers">
<standard>
<![CDATA[
Incrementers in nested loops should use different variable names.
]]>
</standard>
<code_comparison>
<code title="Valid: Two different variables being used to increment.">
<![CDATA[
for ($i = 0; $i < 10; <em>$i++</em>) {
for ($j = 0; $j < 10; <em>$j++</em>) {
}
}
]]>
</code>
<code title="Invalid: Inner incrementer is the same variable name as the outer one.">
<![CDATA[
for ($i = 0; $i < 10; <em>$i++</em>) {
for ($j = 0; $j < 10; <em>$i++</em>) {
}
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,39 @@
<documentation title="Unconditional If Statements">
<standard>
<![CDATA[
If statements that are always evaluated should not be used.
]]>
</standard>
<code_comparison>
<code title="Valid: An if statement that only executes conditionally.">
<![CDATA[
if (<em>$test</em>) {
$var = 1;
}
]]>
</code>
<code title="Invalid: An if statement that is always performed.">
<![CDATA[
if (<em>true</em>) {
$var = 1;
}
]]>
</code>
</code_comparison>
<code_comparison>
<code title="Valid: An if statement that only executes conditionally.">
<![CDATA[
if (<em>$test</em>) {
$var = 1;
}
]]>
</code>
<code title="Invalid: An if statement that is never performed.">
<![CDATA[
if (<em>false</em>) {
$var = 1;
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,29 @@
<documentation title="Unnecessary Final Modifiers">
<standard>
<![CDATA[
Methods should not be declared final inside of classes that are declared final.
]]>
</standard>
<code_comparison>
<code title="Valid: A method in a final class is not marked final.">
<![CDATA[
final class Foo
{
public function bar()
{
}
}
]]>
</code>
<code title="Invalid: A method in a final class is also marked final.">
<![CDATA[
final class Foo
{
public <em>final</em> function bar()
{
}
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,25 @@
<documentation title="Unused function parameters">
<standard>
<![CDATA[
All parameters in a functions signature should be used within the function.
]]>
</standard>
<code_comparison>
<code title="Valid: All the parameters are used.">
<![CDATA[
function addThree($a, $b, $c)
{
return <em>$a + $b + $c</em>;
}
]]>
</code>
<code title="Invalid: One of the parameters is not being used.">
<![CDATA[
function addThree($a, $b, $c)
{
return <em>$a + $b</em>;
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,32 @@
<documentation title="Useless Overriding Methods">
<standard>
<![CDATA[
Methods should not be defined that only call the parent method.
]]>
</standard>
<code_comparison>
<code title="Valid: A method that extends functionality on a parent method.">
<![CDATA[
final class Foo
{
public function bar()
{
parent::bar();
<em>$this->doSomethingElse();</em>
}
}
]]>
</code>
<code title="Invalid: An overriding method that only calls the parent.">
<![CDATA[
final class Foo
{
public function bar()
{
<em>parent::bar();</em>
}
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,25 @@
<documentation title="Todo Comments">
<standard>
<![CDATA[
FIXME Statements should be taken care of.
]]>
</standard>
<code_comparison>
<code title="Valid: A comment without a fixme.">
<![CDATA[
// <em>Handle strange case</em>
if ($test) {
$var = 1;
}
]]>
</code>
<code title="Invalid: A fixme comment.">
<![CDATA[
// <em>FIXME</em>: This needs to be fixed!
if ($test) {
$var = 1;
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,25 @@
<documentation title="Todo Comments">
<standard>
<![CDATA[
TODO Statements should be taken care of.
]]>
</standard>
<code_comparison>
<code title="Valid: A comment without a todo.">
<![CDATA[
// <em>Handle strange case</em>
if ($test) {
$var = 1;
}
]]>
</code>
<code title="Invalid: A todo comment.">
<![CDATA[
// <em>TODO</em>: This needs to be fixed!
if ($test) {
$var = 1;
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,22 @@
<documentation title="Inline Control Structures">
<standard>
<![CDATA[
Control Structures should use braces.
]]>
</standard>
<code_comparison>
<code title="Valid: Braces are used around the control structure.">
<![CDATA[
if ($test) <em>{</em>
$var = 1;
<em>}</em>
]]>
</code>
<code title="Invalid: No braces are used for the control structure..">
<![CDATA[
if ($test)
$var = 1;
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,19 @@
<documentation title="CSSLint">
<standard>
<![CDATA[
All css files should pass the basic csslint tests.
]]>
</standard>
<code_comparison>
<code title="Valid: Valid CSS Syntax is used.">
<![CDATA[
.foo: { width: 100<em></em>%; }
]]>
</code>
<code title="Invalid: The CSS has a typo in it.">
<![CDATA[
.foo: { width: 100<em> </em>%; }
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,19 @@
<documentation title="Closure Linter">
<standard>
<![CDATA[
All javascript files should pass basic Closure Linter tests.
]]>
</standard>
<code_comparison>
<code title="Valid: Valid JS Syntax is used.">
<![CDATA[
var foo = [1, 2<em></em>];
]]>
</code>
<code title="Invalid: Trailing comma in a javascript array.">
<![CDATA[
var foo = [1, 2<em>,</em>];
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,19 @@
<documentation title="JSHint">
<standard>
<![CDATA[
All javascript files should pass basic JSHint tests.
]]>
</standard>
<code_comparison>
<code title="Valid: Valid JS Syntax is used.">
<![CDATA[
<em>var</em> foo = 5;
]]>
</code>
<code title="Invalid: The Javascript is using an undefined variable.">
<![CDATA[
<em></em>foo = 5;
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,7 @@
<documentation title="Byte Order Marks">
<standard>
<![CDATA[
Byte Order Marks that may corrupt your application should not be used. These include 0xefbbbf (UTF-8), 0xfeff (UTF-16 BE) and 0xfffe (UTF-16 LE).
]]>
</standard>
</documentation>

View File

@ -0,0 +1,7 @@
<documentation title="End of File Newline">
<standard>
<![CDATA[
Files should end with a newline character.
]]>
</standard>
</documentation>

View File

@ -0,0 +1,7 @@
<documentation title="No End of File Newline">
<standard>
<![CDATA[
Files should not end with a newline character.
]]>
</standard>
</documentation>

View File

@ -0,0 +1,24 @@
<documentation title="Inline HTML">
<standard>
<![CDATA[
Files that contain php code should only have php code and should not have any "inline html".
]]>
</standard>
<code_comparison>
<code title="Valid: A php file with only php code in it.">
<![CDATA[
<?php
$foo = 'bar';
echo $foo . 'baz';
]]>
</code>
<code title="Invalid: A php file with html in it outside of the php tags.">
<![CDATA[
<em>some string here</em>
<?php
$foo = 'bar';
echo $foo . 'baz';
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,7 @@
<documentation title="Line Endings">
<standard>
<![CDATA[
Unix-style line endings are preferred ("\n" instead of "\r\n").
]]>
</standard>
</documentation>

View File

@ -0,0 +1,7 @@
<documentation title="Line Length">
<standard>
<![CDATA[
It is recommended to keep lines at approximately 80 characters long for better code readability.
]]>
</standard>
</documentation>

View File

@ -0,0 +1,7 @@
<documentation title="Lowercased Filenames">
<standard>
<![CDATA[
Lowercase filenames are required.
]]>
</standard>
</documentation>

View File

@ -0,0 +1,29 @@
<documentation title="One Class Per File">
<standard>
<![CDATA[
There should only be one class defined in a file.
]]>
</standard>
<code_comparison>
<code title="Valid: Only one class in the file.">
<![CDATA[
<?php
<em>class Foo</em>
{
}
]]>
</code>
<code title="Invalid: Multiple classes defined in one file.">
<![CDATA[
<?php
<em>class Foo</em>
{
}
<em>class Bar</em>
{
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -0,0 +1,29 @@
<documentation title="One Interface Per File">
<standard>
<![CDATA[
There should only be one interface defined in a file.
]]>
</standard>
<code_comparison>
<code title="Valid: Only one interface in the file.">
<![CDATA[
<?php
<em>interface Foo</em>
{
}
]]>
</code>
<code title="Invalid: Multiple interfaces defined in one file.">
<![CDATA[
<?php
<em>interface Foo</em>
{
}
<em>interface Bar</em>
{
}
]]>
</code>
</code_comparison>
</documentation>

Some files were not shown because too many files have changed in this diff Show More