--- /dev/null
+{
+ "name": "johngrogg/ics-parser",
+ "description": "ICS Parser",
+ "homepage": "https://github.com/u01jmg3/ics-parser",
+ "keywords": [
+ "icalendar",
+ "ics",
+ "ics-parser",
+ "ical",
+ "ical-parser",
+ "ifb"
+ ],
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Jonathan Goode",
+ "role": "Developer/Owner"
+ },
+ {
+ "name": "John Grogg",
+ "email": "john.grogg@gmail.com",
+ "role": "Developer/Prior Owner"
+ },
+ {
+ "name": "Martin Thoma",
+ "email": "info@martin-thoma.de",
+ "role": "Original Developer"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0",
+ "nesbot/carbon": "~1.18"
+ },
+ "require-dev": {
+ "squizlabs/php_codesniffer": "^3.0"
+ },
+ "autoload": {
+ "psr-0": {
+ "ICal": "src/"
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "This file is @generated automatically"
+ ],
+ "hash": "097319ee817f0c85fb442f1a87d6712a",
+ "content-hash": "f1ba5c00fd5ec3e2250ea13c0833d82b",
+ "packages": [
+ {
+ "name": "nesbot/carbon",
+ "version": "1.22.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/briannesbitt/Carbon.git",
+ "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc",
+ "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "symfony/translation": "~2.6 || ~3.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "~2",
+ "phpunit/phpunit": "~4.0 || ~5.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.23-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Carbon\\": "src/Carbon/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "http://nesbot.com"
+ }
+ ],
+ "description": "A simple API extension for DateTime.",
+ "homepage": "http://carbon.nesbot.com",
+ "keywords": [
+ "date",
+ "datetime",
+ "time"
+ ],
+ "time": "2017-01-16 07:55:07"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
+ "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "time": "2017-10-11 12:05:26"
+ },
+ {
+ "name": "symfony/translation",
+ "version": "v3.3.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation.git",
+ "reference": "409bf229cd552bf7e3faa8ab7e3980b07672073f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/409bf229cd552bf7e3faa8ab7e3980b07672073f",
+ "reference": "409bf229cd552bf7e3faa8ab7e3980b07672073f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.5.9|>=7.0.8",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/config": "<2.8",
+ "symfony/yaml": "<3.3"
+ },
+ "require-dev": {
+ "psr/log": "~1.0",
+ "symfony/config": "~2.8|~3.0",
+ "symfony/intl": "^2.8.18|^3.2.5",
+ "symfony/yaml": "~3.3"
+ },
+ "suggest": {
+ "psr/log": "To use logging capability in translator",
+ "symfony/config": "",
+ "symfony/yaml": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Translation Component",
+ "homepage": "https://symfony.com",
+ "time": "2017-10-02 06:42:24"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+ "reference": "d667e245d5dcd4d7bf80f26f2c947d476b66213e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d667e245d5dcd4d7bf80f26f2c947d476b66213e",
+ "reference": "d667e245d5dcd4d7bf80f26f2c947d476b66213e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0"
+ },
+ "bin": [
+ "bin/phpcs",
+ "bin/phpcbf"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "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"
+ ],
+ "time": "2017-10-16 22:40:25"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=5.3.0"
+ },
+ "platform-dev": []
+}
<?php
+require_once GLM_MEMBERS_EVENTS_PLUGIN_PATH . '/vendor/autoload.php';
+use ICal\ICal;
/**
* detail.php
*
/**
* GLmMembersFront_event_detail
*
+ * Need to update this code for ical parsing.
+ * https://github.com/u01jmg3/ics-parser
+ *
* @uses GlmDataEvents
* @package GlmMemberEvents
* @version 0.0.1
}
public function modelAction( $actionData = false ) {
+ echo '<pre>$_REQUEST: ' . print_r( $_REQUEST, true ) . '</pre>';
if ( $id = filter_var( $_REQUEST['id'], FILTER_VALIDATE_INT ) ) {
echo $this->importIcalFeed( $id );
}
public function importIcalFeed( $feed_id )
{
$start_timer = time();
- $debug = false;
- $clear_data = false;
+ $debug = true;
+ $clear_data = true;
$number_events = 0;
if ( $clear_data ) {
$this->clearData();
// Get the feed data
$filename = $this->fetchIcalFile( $feed_url );
- require GLM_MEMBERS_EVENTS_PLUGIN_PATH . '/classes/icalReader.php';
- $ical = new ical($filename);
- $events = $ical->events();
+ // require GLM_MEMBERS_EVENTS_PLUGIN_PATH . '/classes/icalReader.php';
+ // Setup new ical parser
+ try {
+ // require_once GLM_MEMBERS_EVENTS_PLUGIN_PATH . '/vendor/autoload.php';
+ $ical = new ICal($filename, array(
+ 'defaultSpan' => 2, // Default value
+ 'defaultTimeZone' => 'UTC',
+ 'defaultWeekStart' => 'SU', // Default value
+ 'skipRecurrence' => false, // Default value
+ 'useTimeZoneWithRRules' => false, // Default value
+ ));
+ } catch (\Exception $e) {
+ die($e);
+ }
+ // $ical = new ical($filename);
+ $events = $ical->eventsFromRange( date( 'Y-m-d' ) );
+ // $events = $ical->events();
if ( $debug ) {
- $out .= '<pre>$events: ' . print_r( $events, true ) . '</pre>';
+ // $out .= '<pre>$events: ' . print_r( $events, true ) . '</pre>';
}
+ // echo '<pre>$events: ' . print_r( $events, true ) . '</pre>';
+ // exit;
+
$origTimeZone = date_default_timezone_get();
$wpTimeZone = get_option('timezone_string');
date_default_timezone_set( $wpTimeZone );
if ( $events ) {
- foreach ( $events as $event ) {
+ foreach ( $events as $eventObj ) {
+ $event = (array)$eventObj;
+ $event_id = 0;
+ // Get the starting and ending first to see if we want to
+ // import this event
+ // check format of the dtstart and add time part if needed
+ if ( strlen( $event['dtstart'] ) == 8 ) {
+ $event['dtstart'] .= 'T000000Z';
+ }
+ if ( strlen( $event['dtend'] ) == 8 ) {
+ $event['dtend'] .= 'T000000Z';
+ }
+ $starting = $ical->iCalDateToUnixTimestamp( $event['dtstart'] );
+ // Need to check the dtend to see if they are non-inclusive
+ // They'll have VALUE=DATE:YYYMMDD format
+ if ( isset( $event['dtend'] ) && preg_match( '%VALUE=DATE:(\d{4})(\d{2})(\d{2})%', $event['dtend'], $dParts ) ) {
+ $ending = mktime( 0, 0, 0, $dParts[2], ((int)$dParts[3] - 1), $dParts[1] );
+ } else if ( $event['dtend'] ) {
+ $ending = $ical->iCalDateToUnixTimestamp( $event['dtend'] );
+ } else {
+ $ending = $starting;
+ }
+ // If the ending date is in past then skip it
+ if ( mktime( 0, 0, 1, date('n'), date('j'), date('Y') ) > $ending ) {
+ continue;
+ }
+
if ( $debug ) {
$out .= '<pre>$event: ' . print_r( $event, true ) . '</pre>';
}
$contact = array();
$image = '';
- $event['DESCRIPTION'] = str_replace( '\n', "<br>", $event['DESCRIPTION'] );
- $event['DESCRIPTION'] = str_replace( '\,', ",", $event['DESCRIPTION'] );
- $event['DESCRIPTION'] = str_replace( 'ENCODING=QUOTED-PRINTABLE:', "", $event['DESCRIPTION'] );
- $event['DESCRIPTION'] = str_replace( '=0D=0A', "", $event['DESCRIPTION'] );
- $event['SUMMARY'] = str_replace( 'ENCODING=QUOTED-PRINTABLE:', "", $event['SUMMARY'] );
- $event['SUMMARY'] = str_replace( '\,', ",", $event['SUMMARY'] );
- $intro = substr( strip_tags( $event['DESCRIPTION'] ), 0 ,150);
- if ( isset( $event['CONTACT'] ) ) {
+ $event['description'] = str_replace( '\n', "<br>", $event['description'] );
+ $event['description'] = str_replace( '\,', ",", $event['description'] );
+ $event['description'] = str_replace( 'ENCODING=QUOTED-PRINTABLE:', "", $event['description'] );
+ $event['description'] = str_replace( '=0D=0A', "", $event['description'] );
+ $event['summary'] = str_replace( 'ENCODING=QUOTED-PRINTABLE:', "", $event['summary'] );
+ $event['summary'] = str_replace( '\,', ",", $event['summary'] );
+ $intro = substr( strip_tags( $event['description'] ), 0 ,150);
+ if ( isset( $event['contact'] ) ) {
// Remove the backslashes
- $eventContact = str_replace( '\\', '', $event['CONTACT'] );
+ $eventContact = str_replace( '\\', '', $event['contact'] );
if ( $debug ) {
$out .= '<pre>$eventContact: ' . print_r( $eventContact, true ) . '</pre>';
}
- // Check if the event CONTACT has semi colons in it.
- // If it does then the CONTACT string hold the contact email phone and name.
+ // Check if the event contact has semi colons in it.
+ // If it does then the contact string hold the contact email phone and name.
if ( strpos( $eventContact, ';' ) !== false ) {
$contact_data = explode( ';', $eventContact );
if ( $debug ) {
"SELECT id
FROM " . GLM_MEMBERS_EVENTS_PLUGIN_DB_PREFIX . "events
WHERE ical_uid = %s",
- $event['UID']
+ $event['uid']
)
);
+ if ( $event_id ) {
+ continue;
+ }
$old_image = '';
- if ( isset( $event['ATTACH'] ) ) {
+ if ( isset( $event['attach'] ) ) {
// See if we already have this image.
- $img_url = preg_replace( '%FMTTYPE=image/(jpeg|gif|png)[:]|FILENAME=[^:]*[:]%', '', $event['ATTACH'] );
+ $img_url = preg_replace( '%FMTTYPE=image/(jpeg|gif|png)[:]|FILENAME=[^:]*[:]%', '', $event['attach'] );
if ( $img_url && !preg_match( '%drive.google.com%', $img_url ) ) {
if ( $event_id ) {
$old_image = $this->wpdb->get_var(
}
}
}
+ $created_timestamp = isset( $event['created'] )
+ ? $ical->iCalDateToUnixTimestamp( $event['created'] )
+ : time();
$event_data = array(
'status' => $this->config['status_numb']['Active'],
'ref_type' => 10,
- 'created' => ( isset( $event['CREATED'] ) ? $ical->iCalDateToUnixTimestamp( $event['CREATED'] ) : time() ),
- 'updated' => ( isset( $event['CREATED'] ) ? $ical->iCalDateToUnixTimestamp( $event['CREATED'] ) : time() ),
+ 'created' => date('Y-m-d H:i:s', $created_timestamp),
+ 'updated' => date('Y-m-d H:i:s', $created_timestamp),
'approved' => null,
- 'ical_uid' => $event['UID'],
- 'name' => $event['SUMMARY'],
+ 'ical_uid' => $event['uid'],
+ 'name' => $event['summary'],
'intro' => $intro,
- 'descr' => $event['DESCRIPTION'],
+ 'descr' => $event['description'],
'image' => $image,
- 'url' => ( isset( $event['URL'] ) ? $event['URL']: '' ),
+ 'url' => ( isset( $event['url'] ) ? $event['url']: '' ),
'contact_email' => $contact['email'],
'contact_name' => $contact['name'],
'contact_phone' => $contact['phone'],
// Categories
$category_data = null;
$categoryId = null;
- if ( isset( $event['CATEGORIES'] ) ) {
- $category_data = explode( ',', $event['CATEGORIES'] );
+ if ( isset( $event['categories'] ) ) {
+ $category_data = explode( ',', $event['categories'] );
foreach ( $category_data as $category ) {
$categoryId = $this->getCategoryId( $category );
if ( $categoryId ) {
}
}
// Location Data
- if ( isset( $event['LOCATION'] ) ) {
- $location_data = explode( ',', str_replace( '\\', '', $event['LOCATION'] ) );
+ if ( isset( $event['location'] ) ) {
+ $location_data = explode( ',', str_replace( '\\', '', $event['location'] ) );
// Place will be before the first dash
$placeParts = explode( ' - ', $location_data[0] );
$place = ( isset( $placeParts[0] ) ? $placeParts[0]: '');
$location_format = array(
'%d', '%s', '%s', '%d', '%s', '%s'
);
- if ( isset( $event['GEO'] ) ) {
- $geo_data = explode( ';', $event['GEO'] );
+ if ( isset( $event['geo'] ) ) {
+ $geo_data = explode( ';', $event['geo'] );
$location['lat'] = $geo_data[0];
$location['lon'] = $geo_data[1];
$location_format[] = '%s';
$month_of_year = 4095;
$week_of_month = 63;
- $starting = $ical->iCalDateToUnixTimestamp( $event['DTSTART'] );
- // Need to check the DTEND to see if they are non-inclusive
- // They'll have VALUE=DATE:YYYMMDD format
- if ( isset( $event['DTEND'] ) && preg_match( '%VALUE=DATE:(\d{4})(\d{2})(\d{2})%', $event['DTEND'], $dParts ) ) {
- $ending = mktime( 0, 0, 0, $dParts[2], ((int)$dParts[3] - 1), $dParts[1] );
- } else if ( $event['DTEND'] ) {
- $ending = $ical->iCalDateToUnixTimestamp( $event['DTEND'] );
- } else {
- $ending = $starting;
- }
$from_date = date( 'Y-m-d', $starting );
$to_date = date( 'Y-m-d', $ending );
$etime = date( 'H:i', $ending );
$freq = $byday = $until = '';
- if ( isset( $event['RRULE'] ) ) {
+ if ( isset( $event['rrule'] ) ) {
$recurring_event = 1;
- $rrule_data = explode( ';', $event['RRULE'] );
+ $rrule_data = explode( ';', $event['rrule'] );
if ( $rrule_data ) {
foreach ( $rrule_data as $rule ) {
if ( preg_match( '%FREQ=(.*)%', $rule, $matches ) ) {
foreach ( $days as $day ) {
if ( preg_match( '%([0-9]*)?([A-Z]{2})%', $day, $d_matches ) ) {
if ( $d_matches[1] ) {
- $week_of_month = pow( 2, (int)$d_matches[1] );
+ $week_of_month = pow( 2, ((int)$d_matches[1] - 1) );
}
switch ( $d_matches[2] ) {
case "SU":
}
}
}
- if ( isset( $event['RDATE'] ) && preg_match( '%VALUE=DATE:(.*)%', $event['RDATE'], $matches ) ) {
+ if ( isset( $event['rdate'] ) && preg_match( '%VALUE=DATE:(.*)%', $event['rdate'], $matches ) ) {
$rDates = explode( ',', $matches[1] );
foreach ( $rDates as $key => &$rDate ) {
if ( preg_match( '%([0-9]{4})([0-9]{2})([0-9]{2})%', $rDate, $rDateMatches ) ) {
GLM_MEMBERS_EVENTS_PLUGIN_DB_PREFIX . 'recurrences',
$recur_data,
array(
- '%d',
- '%s',
- '%s',
- '%s',
- '%s',
- '%s',
- '%s',
- '%d',
- '%d',
- '%d',
- '%d',
- '%d',
- '%d',
- '%d',
- '%d',
- '%s'
+ '%d', // event_id
+ '%s', // name
+ '%s', // start_time
+ '%s', // end_time
+ '%s', // start_time_only
+ '%s', // from_date
+ '%s', // to_date
+ '%d', // all_day
+ '%d', // recurring_event
+ '%d', // month_of_year
+ '%d', // week_of_month
+ '%d', // day_of_week
+ '%d', // day_of_month
+ '%d', // by_day_of_month
+ '%d', // last_day_of_month
+ '%s' // serialized_times
)
);
$recurr_id = $this->wpdb->insert_id;
--- /dev/null
+<?php
+
+namespace ICal;
+
+class Event
+{
+ const HTML_TEMPLATE = '<p>%s: %s</p>';
+
+ /**
+ * http://www.kanzaki.com/docs/ical/summary.html
+ *
+ * @var $summary
+ */
+ public $summary;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/dtstart.html
+ *
+ * @var $dtstart
+ */
+ public $dtstart;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/dtend.html
+ *
+ * @var $dtend
+ */
+ public $dtend;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/duration.html
+ *
+ * @var $duration
+ */
+ public $duration;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/dtstamp.html
+ *
+ * @var $dtstamp
+ */
+ public $dtstamp;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/uid.html
+ *
+ * @var $uid
+ */
+ public $uid;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/created.html
+ *
+ * @var $created
+ */
+ public $created;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/lastModified.html
+ *
+ * @var $lastmodified
+ */
+ public $lastmodified;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/description.html
+ *
+ * @var $description
+ */
+ public $description;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/location.html
+ *
+ * @var $location
+ */
+ public $location;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/sequence.html
+ *
+ * @var $sequence
+ */
+ public $sequence;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/status.html
+ *
+ * @var $status
+ */
+ public $status;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/transp.html
+ *
+ * @var $transp
+ */
+ public $transp;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/organizer.html
+ *
+ * @var $organizer
+ */
+ public $organizer;
+
+ /**
+ * http://www.kanzaki.com/docs/ical/attendee.html
+ *
+ * @var $attendee
+ */
+ public $attendee;
+
+ /**
+ * Creates the Event object
+ *
+ * @param array $data
+ * @return void
+ */
+ public function __construct(array $data = array())
+ {
+ if (!empty($data)) {
+ foreach ($data as $key => $value) {
+ $variable = self::snakeCase($key);
+ $this->{$variable} = self::prepareData($value);
+ }
+ }
+ }
+
+ /**
+ * Prepares the data for output
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ protected function prepareData($value)
+ {
+ if (is_string($value)) {
+ return stripslashes(trim(str_replace('\n', "\n", $value)));
+ } elseif (is_array($value)) {
+ return array_map('self::prepareData', $value);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Returns Event data excluding anything blank
+ * within an HTML template
+ *
+ * @param string $html HTML template to use
+ * @return string
+ */
+ public function printData($html = self::HTML_TEMPLATE)
+ {
+ $data = array(
+ 'SUMMARY' => $this->summary,
+ 'DTSTART' => $this->dtstart,
+ 'DTEND' => $this->dtend,
+ 'DTSTART_TZ' => $this->dtstart_tz,
+ 'DTEND_TZ' => $this->dtend_tz,
+ 'DURATION' => $this->duration,
+ 'DTSTAMP' => $this->dtstamp,
+ 'UID' => $this->uid,
+ 'CREATED' => $this->created,
+ 'LAST-MODIFIED' => $this->lastmodified,
+ 'DESCRIPTION' => $this->description,
+ 'LOCATION' => $this->location,
+ 'SEQUENCE' => $this->sequence,
+ 'STATUS' => $this->status,
+ 'TRANSP' => $this->transp,
+ 'ORGANISER' => $this->organizer,
+ 'ATTENDEE(S)' => $this->attendee,
+ );
+
+ $data = array_filter($data); // Remove any blank values
+ $output = '';
+
+ foreach ($data as $key => $value) {
+ $output .= sprintf($html, $key, $value);
+ }
+
+ return $output;
+ }
+
+ /**
+ * Converts the given input to snake_case
+ *
+ * @param string $input
+ * @param string $glue
+ * @param string $separator
+ * @return string
+ */
+ protected static function snakeCase($input, $glue = '_', $separator = '-')
+ {
+ $input = preg_split('/(?<=[a-z])(?=[A-Z])/x', $input);
+ $input = join($input, $glue);
+ $input = str_replace($separator, $glue, $input);
+
+ return strtolower($input);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * This PHP class will read an ICS (`.ics`, `.ical`, `.ifb`) file, parse it and return an
+ * array of its contents.
+ *
+ * PHP 5 (≥ 5.3.0)
+ *
+ * @author Jonathan Goode <https://github.com/u01jmg3>, John Grogg <john.grogg@gmail.com>, Martin Thoma <info@martin-thoma.de>
+ * @license https://opensource.org/licenses/mit-license.php MIT License
+ * @version 2.1.1
+ */
+
+namespace ICal;
+
+use Carbon\Carbon;
+
+class ICal
+{
+ const DATE_TIME_FORMAT = 'Ymd\THis';
+ const DATE_TIME_FORMAT_PRETTY = 'F Y H:i:s';
+ const ICAL_DATE_TIME_TEMPLATE = 'TZID=%s:';
+ const RECURRENCE_EVENT = 'Generated recurrence event';
+ const SECONDS_IN_A_WEEK = 604800;
+ const TIME_FORMAT = 'His';
+ const TIME_ZONE_UTC = 'UTC';
+ const UNIX_FORMAT = 'U';
+ const UNIX_MIN_YEAR = 1970;
+
+ /**
+ * Tracks the number of alarms in the current iCal feed
+ *
+ * @var integer
+ */
+ public $alarmCount = 0;
+
+ /**
+ * Tracks the number of events in the current iCal feed
+ *
+ * @var integer
+ */
+ public $eventCount = 0;
+
+ /**
+ * Tracks the free/busy count in the current iCal feed
+ *
+ * @var integer
+ */
+ public $freeBusyCount = 0;
+
+ /**
+ * Tracks the number of todos in the current iCal feed
+ *
+ * @var integer
+ */
+ public $todoCount = 0;
+
+ /**
+ * The value in years to use for indefinite, recurring events
+ *
+ * @var integer
+ */
+ public $defaultSpan = 2;
+
+ /**
+ * Enables customisation of the default time zone
+ *
+ * @var string
+ */
+ public $defaultTimeZone;
+
+ /**
+ * The two letter representation of the first day of the week
+ *
+ * @var string
+ */
+ public $defaultWeekStart = 'MO';
+
+ /**
+ * Toggles whether to skip the parsing of recurrence rules
+ *
+ * @var boolean
+ */
+ public $skipRecurrence = false;
+
+ /**
+ * Toggles whether to use time zone info when parsing recurrence rules
+ *
+ * @var boolean
+ */
+ public $useTimeZoneWithRRules = false;
+
+ /**
+ * The parsed calendar
+ *
+ * @var array
+ */
+ public $cal = array();
+
+ /**
+ * Tracks the VFREEBUSY component
+ *
+ * @var integer
+ */
+ protected $freeBusyIndex = 0;
+
+ /**
+ * Variable to track the previous keyword
+ *
+ * @var string
+ */
+ protected $lastKeyword;
+
+ /**
+ * Cache valid time zones to avoid unnecessary lookups
+ *
+ * @var array
+ */
+ protected $validTimeZones = array();
+
+ /**
+ * Event recurrence instances that have been altered
+ *
+ * @var array
+ */
+ protected $alteredRecurrenceInstances = array();
+
+ /**
+ * An associative array containing ordinal data
+ *
+ * @var array
+ */
+ protected $dayOrdinals = array(
+ 1 => 'first',
+ 2 => 'second',
+ 3 => 'third',
+ 4 => 'fourth',
+ 5 => 'fifth',
+ );
+
+ /**
+ * An associative array containing weekday conversion data
+ *
+ * @var array
+ */
+ protected $weekdays = array(
+ 'SU' => 'sunday',
+ 'MO' => 'monday',
+ 'TU' => 'tuesday',
+ 'WE' => 'wednesday',
+ 'TH' => 'thursday',
+ 'FR' => 'friday',
+ 'SA' => 'saturday',
+ );
+
+ /**
+ * An associative array containing week conversion data
+ * (UK = SU, Europe = MO)
+ *
+ * @var array
+ */
+ protected $weeks = array(
+ 'SA' => array('SA', 'SU', 'MO', 'TU', 'WE', 'TH', 'FR'),
+ 'SU' => array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'),
+ 'MO' => array('MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'),
+ );
+
+ /**
+ * An associative array containing month names
+ *
+ * @var array
+ */
+ protected $monthNames = array(
+ 1 => 'January',
+ 2 => 'February',
+ 3 => 'March',
+ 4 => 'April',
+ 5 => 'May',
+ 6 => 'June',
+ 7 => 'July',
+ 8 => 'August',
+ 9 => 'September',
+ 10 => 'October',
+ 11 => 'November',
+ 12 => 'December',
+ );
+
+ /**
+ * An associative array containing frequency conversion terms
+ *
+ * @var array
+ */
+ protected $frequencyConversion = array(
+ 'DAILY' => 'day',
+ 'WEEKLY' => 'week',
+ 'MONTHLY' => 'month',
+ 'YEARLY' => 'year',
+ );
+
+ /**
+ * Define which variables can be configured
+ *
+ * @var array
+ */
+ private static $configurableOptions = array(
+ 'defaultSpan',
+ 'defaultTimeZone',
+ 'defaultWeekStart',
+ 'skipRecurrence',
+ 'useTimeZoneWithRRules',
+ );
+
+ /**
+ * Creates the ICal object
+ *
+ * @param mixed $files The path or URL to each ICS file to parse
+ * or iCal content provided as an array
+ * @param array $options Default options to be used by the parser
+ * @return void
+ */
+ public function __construct($files = false, array $options = array())
+ {
+ ini_set('auto_detect_line_endings', '1');
+
+ foreach ($options as $option => $value) {
+ if (in_array($option, self::$configurableOptions)) {
+ $this->{$option} = $value;
+ }
+ }
+
+ // Fallback to use the system default time zone
+ if (!isset($this->defaultTimeZone) || !$this->isValidTimeZoneId($this->defaultTimeZone)) {
+ $this->defaultTimeZone = date_default_timezone_get();
+ }
+
+ if ($files !== false) {
+ $files = is_array($files) ? $files : array($files);
+
+ foreach ($files as $file) {
+ if ($this->isFileOrUrl($file)) {
+ $lines = $this->fileOrUrl($file);
+ } else {
+ $lines = is_array($file) ? $file : array($file);
+ }
+
+ $this->initLines($lines);
+ }
+ }
+ }
+
+ /**
+ * Initialises lines from a string
+ *
+ * @param string $string The contents of the ICS file to initialise
+ * @return ICal
+ */
+ public function initString($string)
+ {
+ if (empty($this->cal)) {
+ $lines = explode(PHP_EOL, $string);
+
+ $this->initLines($lines);
+ } else {
+ trigger_error('ICal::initString: Calendar already initialised in constructor', E_USER_NOTICE);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Initialises lines from a file
+ *
+ * @param string $file The file path or URL of the ICS to use
+ * @return ICal
+ */
+ public function initFile($file)
+ {
+ if (empty($this->cal)) {
+ $lines = $this->fileOrUrl($file);
+
+ $this->initLines($lines);
+ } else {
+ trigger_error('ICal::initFile: Calendar already initialised in constructor', E_USER_NOTICE);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Initialises lines from a URL
+ *
+ * @param string $url The url of the ICS file to download and initialise from
+ * @return ICal
+ */
+ public function initUrl($url)
+ {
+ $this->initFile($url);
+
+ return $this;
+ }
+
+ /**
+ * Initialises the parser using an array
+ * containing each line of iCal content
+ *
+ * @param array $lines The lines to initialise
+ * @return void
+ */
+ protected function initLines(array $lines)
+ {
+ $lines = $this->unfold($lines);
+
+ if (stristr($lines[0], 'BEGIN:VCALENDAR') !== false) {
+ $component = '';
+ foreach ($lines as $line) {
+ $line = rtrim($line); // Trim trailing whitespace
+ $line = $this->removeUnprintableChars($line);
+ $line = $this->cleanData($line);
+ $add = $this->keyValueFromString($line);
+
+ $keyword = $add[0];
+ $values = $add[1]; // May be an array containing multiple values
+
+ if (!is_array($values)) {
+ if (!empty($values)) {
+ $values = array($values); // Make an array as not already
+ $blankArray = array(); // Empty placeholder array
+ array_push($values, $blankArray);
+ } else {
+ $values = array(); // Use blank array to ignore this line
+ }
+ } elseif (empty($values[0])) {
+ $values = array(); // Use blank array to ignore this line
+ }
+
+ // Reverse so that our array of properties is processed first
+ $values = array_reverse($values);
+
+ foreach ($values as $value) {
+ switch ($line) {
+ // http://www.kanzaki.com/docs/ical/vtodo.html
+ case 'BEGIN:VTODO':
+ if (!is_array($value)) {
+ $this->todoCount++;
+ }
+ $component = 'VTODO';
+ break;
+
+ // http://www.kanzaki.com/docs/ical/vevent.html
+ case 'BEGIN:VEVENT':
+ if (!is_array($value)) {
+ $this->eventCount++;
+ }
+ $component = 'VEVENT';
+ break;
+
+ // http://www.kanzaki.com/docs/ical/vfreebusy.html
+ case 'BEGIN:VFREEBUSY':
+ if (!is_array($value)) {
+ $this->freeBusyIndex++;
+ }
+ $component = 'VFREEBUSY';
+ break;
+
+ case 'BEGIN:VALARM':
+ if (!is_array($value)) {
+ $this->alarmCount++;
+ }
+ $component = 'VALARM';
+ break;
+
+ case 'END:VALARM':
+ $component = 'VEVENT';
+ break;
+
+ case 'BEGIN:DAYLIGHT':
+ case 'BEGIN:STANDARD':
+ case 'BEGIN:VCALENDAR':
+ case 'BEGIN:VTIMEZONE':
+ $component = $value;
+ break;
+
+ case 'END:DAYLIGHT':
+ case 'END:STANDARD':
+ case 'END:VCALENDAR':
+ case 'END:VEVENT':
+ case 'END:VFREEBUSY':
+ case 'END:VTIMEZONE':
+ case 'END:VTODO':
+ $component = 'VCALENDAR';
+ break;
+
+ default:
+ $this->addCalendarComponentWithKeyAndValue($component, $keyword, $value);
+ break;
+ }
+ }
+ }
+
+ $this->processEvents();
+
+ if (!$this->skipRecurrence) {
+ $this->processRecurrences();
+ }
+
+ $this->processDateConversions();
+ }
+ }
+
+ /**
+ * Unfolds an iCal file in preparation for parsing
+ * (https://icalendar.org/iCalendar-RFC-5545/3-1-content-lines.html)
+ *
+ * @param array $lines The contents of the iCal string to unfold
+ * @return string
+ */
+ protected function unfold(array $lines)
+ {
+ $string = implode(PHP_EOL, $lines);
+ $string = preg_replace('/' . PHP_EOL . '[ \t]/', '', $string);
+ $lines = explode(PHP_EOL, $string);
+
+ return $lines;
+ }
+
+ /**
+ * Add one key and value pair to the `$this->cal` array
+ *
+ * @param string $component This could be VTODO, VEVENT, VCALENDAR, ...
+ * @param string|boolean $keyword The keyword, for example DTSTART
+ * @param string $value The value, for example 20110105T090000Z
+ * @return void
+ */
+ protected function addCalendarComponentWithKeyAndValue($component, $keyword, $value)
+ {
+ if ($keyword == false) {
+ $keyword = $this->lastKeyword;
+ }
+
+ switch ($component) {
+ case 'VALARM':
+ $key1 = 'VEVENT';
+ $key2 = ($this->eventCount - 1);
+ $key3 = $component;
+
+ if (!isset($this->cal[$key1][$key2][$key3]["{$keyword}_array"])) {
+ $this->cal[$key1][$key2][$key3]["{$keyword}_array"] = array();
+ }
+
+ if (is_array($value)) {
+ // Add array of properties to the end
+ array_push($this->cal[$key1][$key2][$key3]["{$keyword}_array"], $value);
+ } else {
+ if (!isset($this->cal[$key1][$key2][$key3][$keyword])) {
+ $this->cal[$key1][$key2][$key3][$keyword] = $value;
+ }
+
+ if ($this->cal[$key1][$key2][$key3][$keyword] !== $value) {
+ $this->cal[$key1][$key2][$key3][$keyword] .= ',' . $value;
+ }
+ }
+ break;
+
+ case 'VEVENT':
+ $key1 = $component;
+ $key2 = ($this->eventCount - 1);
+
+ if (!isset($this->cal[$key1][$key2]["{$keyword}_array"])) {
+ $this->cal[$key1][$key2]["{$keyword}_array"] = array();
+ }
+
+ if (is_array($value)) {
+ // Add array of properties to the end
+ array_push($this->cal[$key1][$key2]["{$keyword}_array"], $value);
+ } else {
+ if (!isset($this->cal[$key1][$key2][$keyword])) {
+ $this->cal[$key1][$key2][$keyword] = $value;
+ }
+
+ if ($keyword === 'EXDATE') {
+ if (trim($value) === $value) {
+ $array = array_filter(explode(',', $value));
+ $this->cal[$key1][$key2]["{$keyword}_array"][] = $array;
+ } else {
+ $value = explode(',', implode(',', $this->cal[$key1][$key2]["{$keyword}_array"][1]) . trim($value));
+ $this->cal[$key1][$key2]["{$keyword}_array"][1] = $value;
+ }
+ } else {
+ $this->cal[$key1][$key2]["{$keyword}_array"][] = $value;
+
+ if ($keyword === 'DURATION') {
+ $duration = new \DateInterval($value);
+ array_push($this->cal[$key1][$key2]["{$keyword}_array"], $duration);
+ }
+ }
+
+ if ($this->cal[$key1][$key2][$keyword] !== $value) {
+ $this->cal[$key1][$key2][$keyword] .= ',' . $value;
+ }
+ }
+ break;
+
+ case 'VFREEBUSY':
+ $key1 = $component;
+ $key2 = ($this->freeBusyIndex - 1);
+ $key3 = $keyword;
+
+ if ($keyword === 'FREEBUSY') {
+ if (is_array($value)) {
+ $this->cal[$key1][$key2][$key3][][] = $value;
+ } else {
+ $this->freeBusyCount++;
+
+ end($this->cal[$key1][$key2][$key3]);
+ $key = key($this->cal[$key1][$key2][$key3]);
+
+ $value = explode('/', $value);
+ $this->cal[$key1][$key2][$key3][$key][] = $value;
+ }
+ } else {
+ $this->cal[$key1][$key2][$key3][] = $value;
+ }
+ break;
+
+ case 'VTODO':
+ $this->cal[$component][$this->todoCount - 1][$keyword] = $value;
+ break;
+
+ default:
+ $this->cal[$component][$keyword] = $value;
+ break;
+ }
+
+ $this->lastKeyword = $keyword;
+ }
+
+ /**
+ * Gets the key value pair from an iCal string
+ *
+ * @param string $text
+ * @return array
+ */
+ protected function keyValueFromString($text)
+ {
+ $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
+
+ $colon = strpos($text, ':');
+ $quote = strpos($text, '"');
+ if ($colon === false) {
+ $matches = array();
+ } elseif ($quote === false || $colon < $quote) {
+ list($before, $after) = explode(':', $text, 2);
+ $matches = array($text, $before, $after);
+ } else {
+ list($before, $text) = explode('"', $text, 2);
+ $text = '"' . $text;
+ $matches = str_getcsv($text, ':');
+ $combinedValue = '';
+
+ foreach ($matches as $key => $match) {
+ if ($key === 0) {
+ if (!empty($before)) {
+ $matches[$key] = $before . '"' . $matches[$key] . '"';
+ }
+ } else {
+ if ($key > 1) {
+ $combinedValue .= ':';
+ }
+
+ $combinedValue .= $matches[$key];
+ }
+ }
+ $matches = array_slice($matches, 0, 2);
+ $matches[1] = $combinedValue;
+ array_unshift($matches, $before . $text);
+ }
+
+ if (count($matches) === 0) {
+ return false;
+ }
+
+ if (preg_match('/^([A-Z-]+)([;][\w\W]*)?$/', $matches[1])) {
+ $matches = array_splice($matches, 1, 2); // Remove first match and re-align ordering
+
+ // Process properties
+ if (preg_match('/([A-Z-]+)[;]([\w\W]*)/', $matches[0], $properties)) {
+ // Remove first match
+ array_shift($properties);
+ // Fix to ignore everything in keyword after a ; (e.g. Language, TZID, etc.)
+ $matches[0] = $properties[0];
+ array_shift($properties); // Repeat removing first match
+
+ $formatted = array();
+ foreach ($properties as $property) {
+ // Match semicolon separator outside of quoted substrings
+ preg_match_all('~[^' . PHP_EOL . '";]+(?:"[^"\\\]*(?:\\\.[^"\\\]*)*"[^' . PHP_EOL . '";]*)*~', $property, $attributes);
+ // Remove multi-dimensional array and use the first key
+ $attributes = (sizeof($attributes) === 0) ? array($property) : reset($attributes);
+
+ if (is_array($attributes)) {
+ foreach ($attributes as $attribute) {
+ // Match equals sign separator outside of quoted substrings
+ preg_match_all(
+ '~[^' . PHP_EOL . '"=]+(?:"[^"\\\]*(?:\\\.[^"\\\]*)*"[^' . PHP_EOL . '"=]*)*~',
+ $attribute,
+ $values
+ );
+ // Remove multi-dimensional array and use the first key
+ $value = (sizeof($values) === 0) ? null : reset($values);
+
+ if (is_array($value) && isset($value[1])) {
+ // Remove double quotes from beginning and end only
+ $formatted[$value[0]] = trim($value[1], '"');
+ }
+ }
+ }
+ }
+
+ // Assign the keyword property information
+ $properties[0] = $formatted;
+
+ // Add match to beginning of array
+ array_unshift($properties, $matches[1]);
+ $matches[1] = $properties;
+ }
+
+ return $matches;
+ } else {
+ return false; // Ignore this match
+ }
+ }
+
+ /**
+ * Returns a `DateTime` object from an iCal date time format
+ *
+ * @param string $icalDate A Date in the format YYYYMMDD[T]HHMMSS[Z],
+ * YYYYMMDD[T]HHMMSS or
+ * TZID={Time Zone}:YYYYMMDD[T]HHMMSS
+ * @param boolean $forceTimeZone Whether to force the time zone; the event's or the default
+ * @param boolean $forceUtc Whether to force the time zone as UTC
+ * @return DateTime
+ */
+ public function iCalDateToDateTime($icalDate, $forceTimeZone = false, $forceUtc = false)
+ {
+ /**
+ * iCal times may be in 3 formats, (http://www.kanzaki.com/docs/ical/dateTime.html)
+ *
+ * UTC: Has a trailing 'Z'
+ * Floating: No time zone reference specified, no trailing 'Z', use local time
+ * TZID: Set time zone as specified
+ *
+ * Use DateTime class objects to get around limitations with `mktime` and `gmmktime`.
+ * Must have a local time zone set to process floating times.
+ */
+ $pattern = '/\AT?Z?I?D?=?(.*):?'; // [1]: Time zone
+ $pattern .= '([0-9]{4})'; // [2]: YYYY
+ $pattern .= '([0-9]{2})'; // [3]: MM
+ $pattern .= '([0-9]{2})'; // [4]: DD
+ $pattern .= 'T?'; // Time delimiter
+ $pattern .= '([0-9]{0,2})'; // [5]: HH
+ $pattern .= '([0-9]{0,2})'; // [6]: MM
+ $pattern .= '([0-9]{0,2})'; // [7]: SS
+ $pattern .= '(Z?)/'; // [8]: UTC flag
+
+ preg_match($pattern, $icalDate, $date);
+
+ if (empty($date)) {
+ // Default to the initial
+ $dateTime = $icalDate;
+ } else {
+ // A Unix timestamp cannot represent a date prior to 1 Jan 1970
+ $year = $date[2];
+ $isUtc = false;
+
+ if ($year <= self::UNIX_MIN_YEAR) {
+ $eventTimeZone = ltrim(strstr($icalDate, ':', true), 'TZID=');
+
+ if (empty($eventTimeZone)) {
+ $dateTime = new \DateTime($icalDate, new \DateTimeZone($this->defaultTimeZone));
+ } else {
+ $icalDate = ltrim(strstr($icalDate, ':'), ':');
+ $dateTime = new \DateTime($icalDate, new \DateTimeZone($eventTimeZone));
+ }
+ } else {
+ if ($forceTimeZone) {
+ // TZID={Time Zone}:
+ if (isset($date[1])) {
+ $eventTimeZone = rtrim($date[1], ':');
+ }
+
+ if ($date[8] === 'Z') {
+ $isUtc = true;
+ $dateTime = new \DateTime('now', new \DateTimeZone(self::TIME_ZONE_UTC));
+ } elseif (isset($eventTimeZone) && $this->isValidTimeZoneId($eventTimeZone)) {
+ $dateTime = new \DateTime('now', new \DateTimeZone($eventTimeZone));
+ } else {
+ $dateTime = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
+ }
+ } else {
+ if ($forceUtc) {
+ $dateTime = new \DateTime('now', new \DateTimeZone(self::TIME_ZONE_UTC));
+ } else {
+ $dateTime = new \DateTime('now');
+ }
+ }
+
+ $dateTime->setDate((int) $date[2], (int) $date[3], (int) $date[4]);
+ $dateTime->setTime((int) $date[5], (int) $date[6], (int) $date[7]);
+ }
+
+ if ($forceTimeZone && $isUtc) {
+ $dateTime->setTimezone(new \DateTimeZone($this->defaultTimeZone));
+ } elseif ($forceUtc) {
+ $dateTime->setTimezone(new \DateTimeZone(self::TIME_ZONE_UTC));
+ }
+ }
+
+ return $dateTime;
+ }
+
+ /**
+ * Returns a Unix timestamp from an iCal date time format
+ *
+ * @param string $icalDate A Date in the format YYYYMMDD[T]HHMMSS[Z],
+ * YYYYMMDD[T]HHMMSS or
+ * TZID={Time Zone}:YYYYMMDD[T]HHMMSS
+ * @param boolean $forceTimeZone Whether to force the time zone; the event's or the default
+ * @param boolean $forceUtc Whether to force the time zone as UTC
+ * @return integer
+ */
+ public function iCalDateToUnixTimestamp($icalDate, $forceTimeZone = false, $forceUtc = false)
+ {
+ $dateTime = $this->iCalDateToDateTime($icalDate, $forceTimeZone, $forceUtc);
+ $offset = 0;
+
+ if ($forceTimeZone) {
+ $offset = $dateTime->getOffset();
+ }
+
+ return $dateTime->getTimestamp() + $offset;
+ }
+
+ /**
+ * Returns a date adapted to the calendar time zone depending on the event `TZID`
+ *
+ * @param array $event An event
+ * @param string $key An event property (`DTSTART` or `DTEND`)
+ * @param string $format The date format to apply
+ * @return string|boolean
+ */
+ public function iCalDateWithTimeZone(array $event, $key, $format = self::DATE_TIME_FORMAT)
+ {
+ if (!isset($event[$key . '_array']) || !isset($event[$key])) {
+ return false;
+ }
+
+ $dateArray = $event[$key . '_array'];
+ $date = $event[$key];
+
+ if ($key === 'DURATION') {
+ $duration = end($dateArray);
+ $dateTime = $this->parseDuration($event['DTSTART'], $duration, null);
+ } else {
+ $dateTime = new \DateTime($dateArray[1], new \DateTimeZone(self::TIME_ZONE_UTC));
+ $dateTime->setTimezone(new \DateTimeZone($this->calendarTimeZone()));
+ }
+
+ // Force time zone
+ if (isset($dateArray[0]['TZID'])) {
+ if ($this->isValidTimeZoneId($dateArray[0]['TZID'])) {
+ $dateTime->setTimezone(new \DateTimeZone($dateArray[0]['TZID']));
+ } else {
+ $dateTime->setTimezone(new \DateTimeZone($this->defaultTimeZone));
+ }
+ }
+
+ if (is_null($format)) {
+ $output = $dateTime;
+ } else {
+ if ($format === self::UNIX_FORMAT) {
+ $output = $dateTime->getTimestamp();
+ } else {
+ $output = $dateTime->format($format);
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Performs admin tasks on all events as read from the iCal file.
+ * Adds a Unix timestamp to all `{DTSTART|DTEND|RECURRENCE-ID}_array` arrays
+ * Tracks modified recurrence instances
+ *
+ * @return boolean|void
+ */
+ protected function processEvents()
+ {
+ $events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
+
+ if (empty($events)) {
+ return false;
+ }
+
+ foreach ($events as $key => $anEvent) {
+ foreach (array('DTSTART', 'DTEND', 'RECURRENCE-ID') as $type) {
+ if (isset($anEvent[$type])) {
+ $date = $anEvent[$type . '_array'][1];
+ if (isset($anEvent[$type . '_array'][0]['TZID'])) {
+ $date = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent[$type . '_array'][0]['TZID']) . $date;
+ }
+ $anEvent[$type . '_array'][2] = $this->iCalDateToUnixTimestamp($date);
+ $anEvent[$type . '_array'][3] = $date;
+ }
+ }
+
+ if (isset($anEvent['RECURRENCE-ID'])) {
+ $uid = $anEvent['UID'];
+ if (!isset($this->alteredRecurrenceInstances[$uid])) {
+ $this->alteredRecurrenceInstances[$uid] = array();
+ }
+ $recurrenceDateUtc = $this->iCalDateToUnixTimestamp($anEvent['RECURRENCE-ID_array'][3], true, true);
+ $this->alteredRecurrenceInstances[$uid][$key] = $recurrenceDateUtc;
+ }
+
+ $events[$key] = $anEvent;
+ }
+
+ $eventKeysToRemove = array();
+ foreach ($events as $key => $event) {
+ $checks[] = !isset($event['RECURRENCE-ID']);
+ $checks[] = isset($event['UID']);
+ $checks[] = isset($this->alteredRecurrenceInstances[$event['UID']]);
+
+ if ((bool) array_product($checks)) {
+ $eventDtstartUnix = $this->iCalDateToUnixTimestamp($event['DTSTART_array'][3], true, true);
+
+ if (false !== $alteredEventKey = array_search($eventDtstartUnix, $this->alteredRecurrenceInstances[$event['UID']])) {
+ $events[$key] = array_replace_recursive($events[$key], $events[$alteredEventKey]);
+ $eventKeysToRemove[] = $alteredEventKey;
+ }
+ }
+ unset($checks);
+ }
+ $events = array_diff_key($events, array_flip($eventKeysToRemove));
+
+ $this->cal['VEVENT'] = $events;
+ }
+
+ /**
+ * Processes recurrence rules
+ *
+ * @return boolean|void
+ */
+ protected function processRecurrences()
+ {
+ $events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
+
+ $recurrenceEvents = array();
+ $allRecurrenceEvents = array();
+
+ if (empty($events)) {
+ return false;
+ }
+
+ foreach ($events as $anEvent) {
+ if (isset($anEvent['RRULE']) && $anEvent['RRULE'] !== '') {
+ // Tag as generated by a recurrence rule
+ $anEvent['RRULE_array'][2] = self::RECURRENCE_EVENT;
+
+ $isAllDayEvent = (strlen($anEvent['DTSTART_array'][1]) === 8) ? true : false;
+
+ $initialStart = new \DateTime($anEvent['DTSTART_array'][1]);
+ $initialStartOffset = $initialStart->getOffset();
+ $initialStartTimeZoneName = $initialStart->getTimezone()->getName();
+
+ if (isset($anEvent['DTEND'])) {
+ $initialEnd = new \DateTime($anEvent['DTEND_array'][1]);
+ $initialEndOffset = $initialEnd->getOffset();
+ $initialEndTimeZoneName = $initialEnd->getTimezone()->getName();
+ } else {
+ $initialEndTimeZoneName = $initialStartTimeZoneName;
+ }
+
+ // Recurring event, parse RRULE and add appropriate duplicate events
+ $rrules = array();
+ $rruleStrings = explode(';', $anEvent['RRULE']);
+ foreach ($rruleStrings as $s) {
+ list($k, $v) = explode('=', $s);
+ $rrules[$k] = $v;
+ }
+ // Get frequency
+ $frequency = $rrules['FREQ'];
+ // Get Start timestamp
+ $startTimestamp = $initialStart->getTimestamp();
+ if (isset($anEvent['DTEND'])) {
+ $endTimestamp = $initialEnd->getTimestamp();
+ } elseif (isset($anEvent['DURATION'])) {
+ $duration = end($anEvent['DURATION_array']);
+ $endTimestamp = $this->parseDuration($anEvent['DTSTART'], $duration);
+ } else {
+ $endTimestamp = $anEvent['DTSTART_array'][2];
+ }
+ $eventTimestampOffset = $endTimestamp - $startTimestamp;
+ // Get Interval
+ $interval = (isset($rrules['INTERVAL']) && $rrules['INTERVAL'] !== '') ? $rrules['INTERVAL'] : 1;
+
+ $dayNumber = null;
+ $weekday = null;
+
+ if (in_array($frequency, array('MONTHLY', 'YEARLY')) && isset($rrules['BYDAY']) && $rrules['BYDAY'] !== '') {
+ // Deal with BYDAY
+ $byDay = $rrules['BYDAY'];
+ $dayNumber = intval($byDay);
+
+ if (empty($dayNumber)) { // Returns 0 when no number defined in BYDAY
+ if (!isset($rrules['BYSETPOS'])) {
+ $dayNumber = 1; // Set first as default
+ } elseif (is_numeric($rrules['BYSETPOS'])) {
+ $dayNumber = $rrules['BYSETPOS'];
+ }
+ }
+
+ $weekday = substr($byDay, -2);
+ }
+
+ $untilDefault = date_create('now');
+ $untilDefault->modify($this->defaultSpan . ' year');
+ $untilDefault->setTime(23, 59, 59); // End of the day
+
+ // Compute EXDATEs
+ $exdates = $this->parseExdates($anEvent);
+
+ if (isset($rrules['UNTIL'])) {
+ // Get Until
+ $until = strtotime($rrules['UNTIL']);
+ } elseif (isset($rrules['COUNT'])) {
+ $countOrig = (is_numeric($rrules['COUNT']) && $rrules['COUNT'] > 1) ? $rrules['COUNT'] : 0;
+
+ // Increment count by the number of excluded dates
+ $countOrig += sizeof($exdates);
+
+ // Remove one to exclude the occurrence that initialises the rule
+ $count = ($countOrig - 1);
+
+ if ($interval >= 2) {
+ $count += ($count > 0) ? ($count * $interval) : 0;
+ }
+
+ $countNb = 1;
+ $offset = "+{$count} " . $this->frequencyConversion[$frequency];
+ $until = strtotime($offset, $startTimestamp);
+
+ if (in_array($frequency, array('MONTHLY', 'YEARLY'))
+ && isset($rrules['BYDAY']) && $rrules['BYDAY'] !== ''
+ ) {
+ $dtstart = date_create($anEvent['DTSTART']);
+
+ if (!$dtstart) {
+ continue;
+ }
+
+ for ($i = 1; $i <= $count; $i++) {
+ $dtstartClone = clone $dtstart;
+ $dtstartClone->modify('next ' . $this->frequencyConversion[$frequency]);
+ $offset = "{$this->convertDayOrdinalToPositive($dayNumber, $weekday, $dtstartClone)} {$this->weekdays[$weekday]} of " . $dtstartClone->format('F Y H:i:01');
+ $dtstart->modify($offset);
+ }
+
+ // Jumping X months forwards doesn't mean
+ // the end date will fall on the same day defined in BYDAY
+ // Use the largest of these to ensure we are going far enough
+ // in the future to capture our final end day
+ $until = max($until, $dtstart->format(self::UNIX_FORMAT));
+ }
+
+ unset($offset);
+ } else {
+ $until = $untilDefault->getTimestamp();
+ }
+
+ $until = intval($until);
+
+ // Decide how often to add events and do so
+ switch ($frequency) {
+ case 'DAILY':
+ // Simply add a new event each interval of days until UNTIL is reached
+ $offset = "+{$interval} day";
+ $recurringTimestamp = strtotime($offset, $startTimestamp);
+
+ while ($recurringTimestamp <= $until) {
+ $dayRecurringTimestamp = $recurringTimestamp;
+
+ // Adjust time zone from initial event
+ $dayRecurringOffset = 0;
+ if ($this->useTimeZoneWithRRules) {
+ $recurringTimeZone = \DateTime::createFromFormat(self::UNIX_FORMAT, $dayRecurringTimestamp);
+ $recurringTimeZone->setTimezone($initialStart->getTimezone());
+ $dayRecurringOffset = $recurringTimeZone->getOffset();
+ $dayRecurringTimestamp += $dayRecurringOffset;
+ }
+
+ // Add event
+ $anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $dayRecurringTimestamp) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $dayRecurringTimestamp;
+ $anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
+ $anEvent['DTEND_array'][2] += $eventTimestampOffset;
+ $anEvent['DTEND'] = date(
+ self::DATE_TIME_FORMAT,
+ $anEvent['DTEND_array'][2]
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+
+ // Exclusions
+ $isExcluded = array_filter($exdates, function ($exdate) use ($anEvent, $dayRecurringOffset) {
+ return self::isExdateMatch($exdate, $anEvent, $dayRecurringOffset);
+ });
+
+ if (isset($anEvent['UID'])) {
+ $searchDate = $anEvent['DTSTART'];
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $searchDate = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent['DTSTART_array'][0]['TZID']) . $searchDate;
+ }
+
+ if (isset($this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $searchDateUtc = $this->iCalDateToUnixTimestamp($searchDate, true, true);
+ if (in_array($searchDateUtc, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $isExcluded = true;
+ }
+ }
+ }
+
+ if (!$isExcluded) {
+ $anEvent = $this->processEventIcalDateTime($anEvent);
+ $recurrenceEvents[] = $anEvent;
+ $this->eventCount++;
+
+ // If RRULE[COUNT] is reached then break
+ if (isset($rrules['COUNT'])) {
+ $countNb++;
+
+ if ($countNb >= $countOrig) {
+ break;
+ }
+ }
+ }
+
+ // Move forwards
+ $recurringTimestamp = strtotime($offset, $recurringTimestamp);
+ }
+
+ $recurrenceEvents = $this->trimToRecurrenceCount($rrules, $recurrenceEvents);
+ $allRecurrenceEvents = array_merge($allRecurrenceEvents, $recurrenceEvents);
+ $recurrenceEvents = array(); // Reset
+
+ break;
+
+ case 'WEEKLY':
+ // Create offset
+ $offset = "+{$interval} week";
+
+ $wkst = (isset($rrules['WKST']) && in_array($rrules['WKST'], array('SA', 'SU', 'MO'))) ? $rrules['WKST'] : $this->defaultWeekStart;
+ $aWeek = $this->weeks[$wkst];
+ $days = array('SA' => 'Saturday', 'SU' => 'Sunday', 'MO' => 'Monday');
+
+ // Build list of days of week to add events
+ $weekdays = $aWeek;
+
+ if (isset($rrules['BYDAY']) && $rrules['BYDAY'] !== '') {
+ $byDays = explode(',', $rrules['BYDAY']);
+ } else {
+ // A textual representation of a day, two letters (e.g. SU)
+ $byDays = array(mb_substr(strtoupper($initialStart->format('D')), 0, 2));
+ }
+
+ // Get timestamp of first day of start week
+ $weekRecurringTimestamp = (strcasecmp($initialStart->format('l'), $this->weekdays[$wkst]) === 0)
+ ? $startTimestamp
+ : strtotime("last {$days[$wkst]} " . $initialStart->format('H:i:s'), $startTimestamp);
+
+ // Step through weeks
+ while ($weekRecurringTimestamp <= $until) {
+ $dayRecurringTimestamp = $weekRecurringTimestamp;
+
+ // Adjust time zone from initial event
+ $dayRecurringOffset = 0;
+ if ($this->useTimeZoneWithRRules) {
+ $dayRecurringTimeZone = \DateTime::createFromFormat(self::UNIX_FORMAT, $dayRecurringTimestamp);
+ $dayRecurringTimeZone->setTimezone($initialStart->getTimezone());
+ $dayRecurringOffset = $dayRecurringTimeZone->getOffset();
+ $dayRecurringTimestamp += $dayRecurringOffset;
+ }
+
+ foreach ($weekdays as $day) {
+ // Check if day should be added
+ if (in_array($day, $byDays) && $dayRecurringTimestamp > $startTimestamp
+ && $dayRecurringTimestamp <= $until
+ ) {
+ // Add event
+ $anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $dayRecurringTimestamp) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $dayRecurringTimestamp;
+ $anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
+ $anEvent['DTEND_array'][2] += $eventTimestampOffset;
+ $anEvent['DTEND'] = date(
+ self::DATE_TIME_FORMAT,
+ $anEvent['DTEND_array'][2]
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+
+ // Exclusions
+ $isExcluded = array_filter($exdates, function ($exdate) use ($anEvent, $dayRecurringOffset) {
+ return self::isExdateMatch($exdate, $anEvent, $dayRecurringOffset);
+ });
+
+ if (isset($anEvent['UID'])) {
+ $searchDate = $anEvent['DTSTART'];
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $searchDate = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent['DTSTART_array'][0]['TZID']) . $searchDate;
+ }
+
+ if (isset($this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $searchDateUtc = $this->iCalDateToUnixTimestamp($searchDate, true, true);
+ if (in_array($searchDateUtc, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $isExcluded = true;
+ }
+ }
+ }
+
+ if (!$isExcluded) {
+ $anEvent = $this->processEventIcalDateTime($anEvent);
+ $recurrenceEvents[] = $anEvent;
+ $this->eventCount++;
+
+ // If RRULE[COUNT] is reached then break
+ if (isset($rrules['COUNT'])) {
+ $countNb++;
+
+ if ($countNb >= $countOrig) {
+ break 2;
+ }
+ }
+ }
+ }
+
+ // Move forwards a day
+ $dayRecurringTimestamp = strtotime('+1 day', $dayRecurringTimestamp);
+ }
+
+ // Move forwards $interval weeks
+ $weekRecurringTimestamp = strtotime($offset, $weekRecurringTimestamp);
+ }
+
+ $recurrenceEvents = $this->trimToRecurrenceCount($rrules, $recurrenceEvents);
+ $allRecurrenceEvents = array_merge($allRecurrenceEvents, $recurrenceEvents);
+ $recurrenceEvents = array(); // Reset
+
+ break;
+
+ case 'MONTHLY':
+ // Create offset
+ $recurringTimestamp = $startTimestamp;
+ $offset = "+{$interval} month";
+
+ if (isset($rrules['BYMONTHDAY']) && $rrules['BYMONTHDAY'] !== '') {
+ // Deal with BYMONTHDAY
+ $monthdays = explode(',', $rrules['BYMONTHDAY']);
+
+ while ($recurringTimestamp <= $until) {
+ foreach ($monthdays as $key => $monthday) {
+ if ($key === 0) {
+ // Ensure original event conforms to monthday rule
+ $anEvent['DTSTART'] = gmdate(
+ 'Ym' . sprintf('%02d', $monthday) . '\T' . self::TIME_FORMAT,
+ strtotime($anEvent['DTSTART'])
+ ) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+
+ $anEvent['DTEND'] = gmdate(
+ 'Ym' . sprintf('%02d', $monthday) . '\T' . self::TIME_FORMAT,
+ isset($anEvent['DURATION'])
+ ? $this->parseDuration($anEvent['DTSTART'], end($anEvent['DURATION_array']))
+ : strtotime($anEvent['DTEND'])
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $this->iCalDateToUnixTimestamp($anEvent['DTSTART']);
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+ $anEvent['DTEND_array'][2] = $this->iCalDateToUnixTimestamp($anEvent['DTEND']);
+
+ // Ensure recurring timestamp confirms to BYMONTHDAY rule
+ $monthRecurringTimestamp = $this->iCalDateToUnixTimestamp(
+ gmdate(
+ 'Ym' . sprintf('%02d', $monthday) . '\T' . self::TIME_FORMAT,
+ $recurringTimestamp
+ ) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '')
+ );
+ }
+
+ // Adjust time zone from initial event
+ $monthRecurringOffset = 0;
+ if ($this->useTimeZoneWithRRules) {
+ $recurringTimeZone = \DateTime::createFromFormat(self::UNIX_FORMAT, $monthRecurringTimestamp);
+ $recurringTimeZone->setTimezone($initialStart->getTimezone());
+ $monthRecurringOffset = $recurringTimeZone->getOffset();
+ $monthRecurringTimestamp += $monthRecurringOffset;
+ }
+
+ // Add event
+ $anEvent['DTSTART'] = date(
+ 'Ym' . sprintf('%02d', $monthday) . '\T' . self::TIME_FORMAT,
+ $monthRecurringTimestamp
+ ) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $monthRecurringTimestamp;
+ $anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
+ $anEvent['DTEND_array'][2] += $eventTimestampOffset;
+ $anEvent['DTEND'] = date(
+ self::DATE_TIME_FORMAT,
+ $anEvent['DTEND_array'][2]
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+
+ // Exclusions
+ $isExcluded = array_filter($exdates, function ($exdate) use ($anEvent, $monthRecurringOffset) {
+ return self::isExdateMatch($exdate, $anEvent, $monthRecurringOffset);
+ });
+
+ if (isset($anEvent['UID'])) {
+ $searchDate = $anEvent['DTSTART'];
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $searchDate = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent['DTSTART_array'][0]['TZID']) . $searchDate;
+ }
+
+ if (isset($this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $searchDateUtc = $this->iCalDateToUnixTimestamp($searchDate, true, true);
+ if (in_array($searchDateUtc, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $isExcluded = true;
+ }
+ }
+ }
+
+ if (!$isExcluded) {
+ $anEvent = $this->processEventIcalDateTime($anEvent);
+ $recurrenceEvents[] = $anEvent;
+ $this->eventCount++;
+
+ // If RRULE[COUNT] is reached then break
+ if (isset($rrules['COUNT'])) {
+ $countNb++;
+
+ if ($countNb >= $countOrig) {
+ break 2;
+ }
+ }
+ }
+ }
+
+ // Move forwards
+ $recurringTimestamp = strtotime($offset, $recurringTimestamp);
+ }
+ } elseif (isset($rrules['BYDAY']) && $rrules['BYDAY'] !== '') {
+ while ($recurringTimestamp <= $until) {
+ $monthRecurringTimestamp = $recurringTimestamp;
+
+ // Adjust time zone from initial event
+ $monthRecurringOffset = 0;
+ if ($this->useTimeZoneWithRRules) {
+ $recurringTimeZone = \DateTime::createFromFormat(self::UNIX_FORMAT, $monthRecurringTimestamp);
+ $recurringTimeZone->setTimezone($initialStart->getTimezone());
+ $monthRecurringOffset = $recurringTimeZone->getOffset();
+ $monthRecurringTimestamp += $monthRecurringOffset;
+ }
+
+ $eventStartDesc = "{$this->convertDayOrdinalToPositive($dayNumber, $weekday, $monthRecurringTimestamp)} {$this->weekdays[$weekday]} of "
+ . date(self::DATE_TIME_FORMAT_PRETTY, $monthRecurringTimestamp);
+ $eventStartTimestamp = strtotime($eventStartDesc);
+
+ if (intval($rrules['BYDAY']) === 0) {
+ $lastDayDesc = "last {$this->weekdays[$weekday]} of "
+ . date(self::DATE_TIME_FORMAT_PRETTY, $monthRecurringTimestamp);
+ } else {
+ $lastDayDesc = "{$this->convertDayOrdinalToPositive($dayNumber, $weekday, $monthRecurringTimestamp)} {$this->weekdays[$weekday]} of "
+ . date(self::DATE_TIME_FORMAT_PRETTY, $monthRecurringTimestamp);
+ }
+ $lastDayTimestamp = strtotime($lastDayDesc);
+
+ do {
+ // Prevent 5th day of a month from showing up on the next month
+ // If BYDAY and the event falls outside the current month, skip the event
+
+ $compareCurrentMonth = date('F', $monthRecurringTimestamp);
+ $compareEventMonth = date('F', $eventStartTimestamp);
+
+ if ($compareCurrentMonth !== $compareEventMonth) {
+ $monthRecurringTimestamp = strtotime($offset, $monthRecurringTimestamp);
+ continue;
+ }
+
+ if ($eventStartTimestamp > $startTimestamp && $eventStartTimestamp < $until) {
+ $anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $eventStartTimestamp) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $eventStartTimestamp;
+ $anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
+ $anEvent['DTEND_array'][2] += $eventTimestampOffset;
+ $anEvent['DTEND'] = date(
+ self::DATE_TIME_FORMAT,
+ $anEvent['DTEND_array'][2]
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+
+ // Exclusions
+ $isExcluded = array_filter($exdates, function ($exdate) use ($anEvent, $monthRecurringOffset) {
+ return self::isExdateMatch($exdate, $anEvent, $monthRecurringOffset);
+ });
+
+ if (isset($anEvent['UID'])) {
+ $searchDate = $anEvent['DTSTART'];
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $searchDate = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent['DTSTART_array'][0]['TZID']) . $searchDate;
+ }
+
+ if (isset($this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $searchDateUtc = $this->iCalDateToUnixTimestamp($searchDate, true, true);
+ if (in_array($searchDateUtc, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $isExcluded = true;
+ }
+ }
+ }
+
+ if (!$isExcluded) {
+ $anEvent = $this->processEventIcalDateTime($anEvent);
+ $recurrenceEvents[] = $anEvent;
+ $this->eventCount++;
+
+ // If RRULE[COUNT] is reached then break
+ if (isset($rrules['COUNT'])) {
+ $countNb++;
+
+ if ($countNb >= $countOrig) {
+ break 2;
+ }
+ }
+ }
+ }
+
+ if (isset($rrules['BYSETPOS'])) {
+ // BYSETPOS is defined so skip
+ // looping through each week
+ $lastDayTimestamp = $eventStartTimestamp;
+ }
+
+ $eventStartTimestamp += self::SECONDS_IN_A_WEEK;
+ } while ($eventStartTimestamp <= $lastDayTimestamp);
+
+ // Move forwards
+ $recurringTimestamp = strtotime($offset, $recurringTimestamp);
+ }
+ }
+
+ $recurrenceEvents = $this->trimToRecurrenceCount($rrules, $recurrenceEvents);
+ $allRecurrenceEvents = array_merge($allRecurrenceEvents, $recurrenceEvents);
+ $recurrenceEvents = array(); // Reset
+
+ break;
+
+ case 'YEARLY':
+ // Create offset
+ $recurringTimestamp = $startTimestamp;
+ $offset = "+{$interval} year";
+
+ // Deal with BYMONTH
+ if (isset($rrules['BYMONTH']) && $rrules['BYMONTH'] !== '') {
+ $bymonths = explode(',', $rrules['BYMONTH']);
+ }
+
+ // Check if BYDAY rule exists
+ if (isset($rrules['BYDAY']) && $rrules['BYDAY'] !== '') {
+ while ($recurringTimestamp <= $until) {
+ $yearRecurringTimestamp = $recurringTimestamp;
+
+ // Adjust time zone from initial event
+ $yearRecurringOffset = 0;
+ if ($this->useTimeZoneWithRRules) {
+ $recurringTimeZone = \DateTime::createFromFormat(self::UNIX_FORMAT, $yearRecurringTimestamp);
+ $recurringTimeZone->setTimezone($initialStart->getTimezone());
+ $yearRecurringOffset = $recurringTimeZone->getOffset();
+ $yearRecurringTimestamp += $yearRecurringOffset;
+ }
+
+ foreach ($bymonths as $bymonth) {
+ $eventStartDesc = "{$this->convertDayOrdinalToPositive($dayNumber, $weekday, $yearRecurringTimestamp)} {$this->weekdays[$weekday]}"
+ . " of {$this->monthNames[$bymonth]} "
+ . gmdate('Y H:i:s', $yearRecurringTimestamp);
+ $eventStartTimestamp = strtotime($eventStartDesc);
+
+ if (intval($rrules['BYDAY']) === 0) {
+ $lastDayDesc = "last {$this->weekdays[$weekday]}"
+ . " of {$this->monthNames[$bymonth]} "
+ . gmdate('Y H:i:s', $yearRecurringTimestamp);
+ } else {
+ $lastDayDesc = "{$this->convertDayOrdinalToPositive($dayNumber, $weekday, $yearRecurringTimestamp)} {$this->weekdays[$weekday]}"
+ . " of {$this->monthNames[$bymonth]} "
+ . gmdate('Y H:i:s', $yearRecurringTimestamp);
+ }
+ $lastDayTimestamp = strtotime($lastDayDesc);
+
+ do {
+ if ($eventStartTimestamp > $startTimestamp && $eventStartTimestamp < $until) {
+ $anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $eventStartTimestamp) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $eventStartTimestamp;
+ $anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
+ $anEvent['DTEND_array'][2] += $eventTimestampOffset;
+ $anEvent['DTEND'] = date(
+ self::DATE_TIME_FORMAT,
+ $anEvent['DTEND_array'][2]
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+
+ // Exclusions
+ $isExcluded = array_filter($exdates, function ($exdate) use ($anEvent, $yearRecurringOffset) {
+ return self::isExdateMatch($exdate, $anEvent, $yearRecurringOffset);
+ });
+
+ if (isset($anEvent['UID'])) {
+ $searchDate = $anEvent['DTSTART'];
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $searchDate = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent['DTSTART_array'][0]['TZID']) . $searchDate;
+ }
+
+ if (isset($this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $searchDateUtc = $this->iCalDateToUnixTimestamp($searchDate, true, true);
+ if (in_array($searchDateUtc, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $isExcluded = true;
+ }
+ }
+ }
+
+ if (!$isExcluded) {
+ $anEvent = $this->processEventIcalDateTime($anEvent);
+ $recurrenceEvents[] = $anEvent;
+ $this->eventCount++;
+
+ // If RRULE[COUNT] is reached then break
+ if (isset($rrules['COUNT'])) {
+ $countNb++;
+
+ if ($countNb >= $countOrig) {
+ break 3;
+ }
+ }
+ }
+ }
+
+ $eventStartTimestamp += self::SECONDS_IN_A_WEEK;
+ } while ($eventStartTimestamp <= $lastDayTimestamp);
+ }
+
+ // Move forwards
+ $recurringTimestamp = strtotime($offset, $recurringTimestamp);
+ }
+ } else {
+ $day = $initialStart->format('d');
+
+ // Step through years
+ while ($recurringTimestamp <= $until) {
+ $yearRecurringTimestamp = $recurringTimestamp;
+
+ // Adjust time zone from initial event
+ $yearRecurringOffset = 0;
+ if ($this->useTimeZoneWithRRules) {
+ $recurringTimeZone = \DateTime::createFromFormat(self::UNIX_FORMAT, $yearRecurringTimestamp);
+ $recurringTimeZone->setTimezone($initialStart->getTimezone());
+ $yearRecurringOffset = $recurringTimeZone->getOffset();
+ $yearRecurringTimestamp += $yearRecurringOffset;
+ }
+
+ $eventStartDescs = array();
+ if (isset($rrules['BYMONTH']) && $rrules['BYMONTH'] !== '') {
+ foreach ($bymonths as $bymonth) {
+ array_push($eventStartDescs, "$day {$this->monthNames[$bymonth]} " . gmdate('Y H:i:s', $yearRecurringTimestamp));
+ }
+ } else {
+ array_push($eventStartDescs, $day . gmdate(self::DATE_TIME_FORMAT_PRETTY, $yearRecurringTimestamp));
+ }
+
+ foreach ($eventStartDescs as $eventStartDesc) {
+ $eventStartTimestamp = strtotime($eventStartDesc);
+
+ if ($eventStartTimestamp > $startTimestamp && $eventStartTimestamp < $until) {
+ $anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $eventStartTimestamp) . ($isAllDayEvent || ($initialStartTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
+ $anEvent['DTSTART_array'][2] = $eventStartTimestamp;
+ $anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
+ $anEvent['DTEND_array'][2] += $eventTimestampOffset;
+ $anEvent['DTEND'] = date(
+ self::DATE_TIME_FORMAT,
+ $anEvent['DTEND_array'][2]
+ ) . ($isAllDayEvent || ($initialEndTimeZoneName === 'Z') ? 'Z' : '');
+ $anEvent['DTEND_array'][1] = $anEvent['DTEND'];
+
+ // Exclusions
+ $isExcluded = array_filter($exdates, function ($exdate) use ($anEvent, $yearRecurringOffset) {
+ return self::isExdateMatch($exdate, $anEvent, $yearRecurringOffset);
+ });
+
+ if (isset($anEvent['UID'])) {
+ $searchDate = $anEvent['DTSTART'];
+ if (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $searchDate = sprintf(self::ICAL_DATE_TIME_TEMPLATE, $anEvent['DTSTART_array'][0]['TZID']) . $searchDate;
+ }
+
+ if (isset($this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $searchDateUtc = $this->iCalDateToUnixTimestamp($searchDate, true, true);
+ if (in_array($searchDateUtc, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
+ $isExcluded = true;
+ }
+ }
+ }
+
+ if (!$isExcluded) {
+ $anEvent = $this->processEventIcalDateTime($anEvent);
+ $recurrenceEvents[] = $anEvent;
+ $this->eventCount++;
+
+ // If RRULE[COUNT] is reached then break
+ if (isset($rrules['COUNT'])) {
+ $countNb++;
+
+ if ($countNb >= $countOrig) {
+ break 2;
+ }
+ }
+ }
+ }
+ }
+
+ // Move forwards
+ $recurringTimestamp = strtotime($offset, $recurringTimestamp);
+ }
+ }
+
+ $recurrenceEvents = $this->trimToRecurrenceCount($rrules, $recurrenceEvents);
+ $allRecurrenceEvents = array_merge($allRecurrenceEvents, $recurrenceEvents);
+ $recurrenceEvents = array(); // Reset
+
+ break;
+ }
+ }
+ }
+
+ $events = array_merge($events, $allRecurrenceEvents);
+
+ $this->cal['VEVENT'] = $events;
+ }
+
+ /**
+ * Processes date conversions using the time zone
+ *
+ * Add keys `DTSTART_tz` and `DTEND_tz` to each Event
+ * These keys contain dates adapted to the calendar
+ * time zone depending on the event `TZID`.
+ *
+ * @return boolean|void
+ */
+ protected function processDateConversions()
+ {
+ $events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
+
+ if (empty($events)) {
+ return false;
+ }
+
+ foreach ($events as $key => $anEvent) {
+ if (!$this->isValidDate($anEvent['DTSTART'])) {
+ unset($events[$key]);
+ $this->eventCount--;
+
+ continue;
+ }
+
+ if ($this->useTimeZoneWithRRules && isset($anEvent['RRULE_array'][2]) && $anEvent['RRULE_array'][2] === self::RECURRENCE_EVENT) {
+ $events[$key]['DTSTART_tz'] = $anEvent['DTSTART'];
+ $events[$key]['DTEND_tz'] = $anEvent['DTEND'];
+ } else {
+ $events[$key]['DTSTART_tz'] = $this->iCalDateWithTimeZone($anEvent, 'DTSTART');
+
+ if ($this->iCalDateWithTimeZone($anEvent, 'DTEND')) {
+ $events[$key]['DTEND_tz'] = $this->iCalDateWithTimeZone($anEvent, 'DTEND');
+ } elseif ($this->iCalDateWithTimeZone($anEvent, 'DURATION')) {
+ $events[$key]['DTEND_tz'] = $this->iCalDateWithTimeZone($anEvent, 'DURATION');
+ }
+ }
+ }
+
+ $this->cal['VEVENT'] = $events;
+ }
+
+ /**
+ * Extends the `{DTSTART|DTEND|RECURRENCE-ID}_array`
+ * array to include an iCal date time for each event
+ * (`TZID=Timezone:YYYYMMDD[T]HHMMSS`)
+ *
+ * @param array $event
+ * @param integer $index
+ * @return array
+ */
+ protected function processEventIcalDateTime(array $event, $index = 3)
+ {
+ $calendarTimeZone = $this->calendarTimeZone(true);
+
+ foreach (array('DTSTART', 'DTEND', 'RECURRENCE-ID') as $type) {
+ if (isset($event["{$type}_array"])) {
+ $timeZone = (isset($event["{$type}_array"][0]['TZID'])) ? $event["{$type}_array"][0]['TZID'] : $calendarTimeZone;
+ $event["{$type}_array"][$index] = ((is_null($timeZone)) ? '' : sprintf(self::ICAL_DATE_TIME_TEMPLATE, $timeZone)) . $event["{$type}_array"][1];
+ }
+ }
+
+ return $event;
+ }
+
+ /**
+ * Returns an array of Events.
+ * Every event is a class with the event
+ * details being properties within it.
+ *
+ * @return array
+ */
+ public function events()
+ {
+ $array = $this->cal;
+ $array = isset($array['VEVENT']) ? $array['VEVENT'] : array();
+ $events = array();
+
+ if (!empty($array)) {
+ foreach ($array as $event) {
+ $events[] = new Event($event);
+ }
+ }
+
+ return $events;
+ }
+
+ /**
+ * Returns the calendar name
+ *
+ * @return string
+ */
+ public function calendarName()
+ {
+ return isset($this->cal['VCALENDAR']['X-WR-CALNAME']) ? $this->cal['VCALENDAR']['X-WR-CALNAME'] : '';
+ }
+
+ /**
+ * Returns the calendar description
+ *
+ * @return string
+ */
+ public function calendarDescription()
+ {
+ return isset($this->cal['VCALENDAR']['X-WR-CALDESC']) ? $this->cal['VCALENDAR']['X-WR-CALDESC'] : '';
+ }
+
+ /**
+ * Returns the calendar time zone
+ *
+ * @param boolean $ignoreUtc
+ * @return string
+ */
+ public function calendarTimeZone($ignoreUtc = false)
+ {
+ if (isset($this->cal['VCALENDAR']['X-WR-TIMEZONE'])) {
+ $timeZone = $this->cal['VCALENDAR']['X-WR-TIMEZONE'];
+ } elseif (isset($this->cal['VTIMEZONE']['TZID'])) {
+ $timeZone = $this->cal['VTIMEZONE']['TZID'];
+ } else {
+ $timeZone = $this->defaultTimeZone;
+ }
+
+ // Use default time zone if the calendar's is invalid
+ if (!$this->isValidTimeZoneId($timeZone)) {
+ $timeZone = $this->defaultTimeZone;
+ }
+
+ if ($ignoreUtc && strtoupper($timeZone) === self::TIME_ZONE_UTC) {
+ return null;
+ }
+
+ return $timeZone;
+ }
+
+ /**
+ * Returns an array of arrays with all free/busy events.
+ * Every event is an associative array and each property
+ * is an element it.
+ *
+ * @return array
+ */
+ public function freeBusyEvents()
+ {
+ $array = $this->cal;
+
+ return isset($array['VFREEBUSY']) ? $array['VFREEBUSY'] : '';
+ }
+
+ /**
+ * Returns a boolean value whether the
+ * current calendar has events or not
+ *
+ * @return boolean
+ */
+ public function hasEvents()
+ {
+ return (count($this->events()) > 0) ?: false;
+ }
+
+ /**
+ * Returns a sorted array of the events in a given range,
+ * or an empty array if no events exist in the range.
+ *
+ * Events will be returned if the start or end date is contained within the
+ * range (inclusive), or if the event starts before and end after the range.
+ *
+ * If a start date is not specified or of a valid format, then the start
+ * of the range will default to the current time and date of the server.
+ *
+ * If an end date is not specified or of a valid format, then the end of
+ * the range will default to the current time and date of the server,
+ * plus 20 years.
+ *
+ * Note that this function makes use of Unix timestamps. This might be a
+ * problem for events on, during, or after 29 Jan 2038.
+ * See https://en.wikipedia.org/wiki/Unix_time#Representing_the_number
+ *
+ * @param string $rangeStart Start date of the search range.
+ * @param string $rangeEnd End date of the search range.
+ * @return array
+ * @throws Exception
+ */
+ public function eventsFromRange($rangeStart = false, $rangeEnd = false)
+ {
+ // Sort events before processing range
+ $events = $this->sortEventsWithOrder($this->events(), SORT_ASC);
+
+ if (empty($events)) {
+ return array();
+ }
+
+ $extendedEvents = array();
+
+ if ($rangeStart) {
+ try {
+ $rangeStart = new \DateTime($rangeStart, new \DateTimeZone($this->defaultTimeZone));
+ } catch (\Exception $e) {
+ error_log("ICal::eventsFromRange: Invalid date passed ({$rangeStart})");
+ $rangeStart = false;
+ }
+ } else {
+ $rangeStart = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
+ }
+
+ if ($rangeEnd) {
+ try {
+ $rangeEnd = new \DateTime($rangeEnd, new \DateTimeZone($this->defaultTimeZone));
+ } catch (\Exception $e) {
+ error_log("ICal::eventsFromRange: Invalid date passed ({$rangeEnd})");
+ $rangeEnd = false;
+ }
+ } else {
+ $rangeEnd = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
+ $rangeEnd->modify('+20 years');
+ }
+
+ // If start and end are identical and are dates with no times...
+ if ($rangeEnd->format('His') == 0 && $rangeStart->getTimestamp() == $rangeEnd->getTimestamp()) {
+ $rangeEnd->modify('+1 day');
+ }
+
+ $rangeStart = $rangeStart->getTimestamp();
+ $rangeEnd = $rangeEnd->getTimestamp();
+
+ foreach ($events as $anEvent) {
+ $eventStart = $anEvent->dtstart_array[2];
+ $eventEnd = (isset($anEvent->dtend_array[2])) ? $anEvent->dtend_array[2] : null;
+
+ if (($eventStart >= $rangeStart && $eventStart < $rangeEnd) // Event start date contained in the range
+ || ($eventEnd !== null
+ && (
+ ($eventEnd > $rangeStart && $eventEnd <= $rangeEnd) // Event end date contained in the range
+ || ($eventStart < $rangeStart && $eventEnd > $rangeEnd) // Event starts before and finishes after range
+ )
+ )
+ ) {
+ $extendedEvents[] = $anEvent;
+ }
+ }
+
+ if (empty($extendedEvents)) {
+ return array();
+ }
+
+ return $extendedEvents;
+ }
+
+ /**
+ * Returns a sorted array of the events following a given string,
+ * or `false` if no events exist in the range.
+ *
+ * @param string $interval
+ * @return array
+ */
+ public function eventsFromInterval($interval)
+ {
+ $rangeStart = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
+ $rangeEnd = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
+
+ $dateInterval = \DateInterval::createFromDateString($interval);
+ $rangeEnd->add($dateInterval);
+
+ return $this->eventsFromRange($rangeStart->format('Y-m-d'), $rangeEnd->format('Y-m-d'));
+ }
+
+ /**
+ * Sorts events based on a given sort order
+ *
+ * @param array $events An array of Events
+ * @param integer $sortOrder Either SORT_ASC, SORT_DESC, SORT_REGULAR, SORT_NUMERIC, SORT_STRING
+ * @return array
+ */
+ public function sortEventsWithOrder(array $events, $sortOrder = SORT_ASC)
+ {
+ $extendedEvents = array();
+ $timestamp = array();
+
+ foreach ($events as $key => $anEvent) {
+ $extendedEvents[] = $anEvent;
+ $timestamp[$key] = $anEvent->dtstart_array[2];
+ }
+
+ array_multisort($timestamp, $sortOrder, $extendedEvents);
+
+ return $extendedEvents;
+ }
+
+ /**
+ * Checks if a time zone is valid
+ *
+ * @param string $timeZone
+ * @return boolean
+ */
+ protected function isValidTimeZoneId($timeZone)
+ {
+ if (in_array($timeZone, $this->validTimeZones)) {
+ return true;
+ }
+ $valid = array();
+ $tza = timezone_abbreviations_list();
+
+ foreach ($tza as $zone) {
+ foreach ($zone as $item) {
+ $valid[$item['timezone_id']] = true;
+ }
+ }
+
+ unset($valid['']);
+
+ if (isset($valid[$timeZone]) || in_array($timeZone, timezone_identifiers_list(\DateTimeZone::ALL_WITH_BC))) {
+ $this->validTimeZones[] = $timeZone;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Parses a duration and applies it to a date
+ *
+ * @param string $date A date to add a duration to
+ * @param string $duration A duration to parse
+ * @param string $format The format to apply to the DateTime object
+ * @return integer|DateTime
+ */
+ protected function parseDuration($date, $duration, $format = self::UNIX_FORMAT)
+ {
+ $dateTime = date_create($date);
+ $dateTime->modify($duration->y . ' year');
+ $dateTime->modify($duration->m . ' month');
+ $dateTime->modify($duration->d . ' day');
+ $dateTime->modify($duration->h . ' hour');
+ $dateTime->modify($duration->i . ' minute');
+ $dateTime->modify($duration->s . ' second');
+
+ if (is_null($format)) {
+ $output = $dateTime;
+ } else {
+ if ($format === self::UNIX_FORMAT) {
+ $output = $dateTime->getTimestamp();
+ } else {
+ $output = $dateTime->format($format);
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Gets the number of days between a start and end date
+ *
+ * @param integer $days
+ * @param integer $start
+ * @param integer $end
+ * @return integer
+ */
+ protected function numberOfDays($days, $start, $end)
+ {
+ $w = array(date('w', $start), date('w', $end));
+ $oneWeek = self::SECONDS_IN_A_WEEK;
+ $x = floor(($end - $start) / $oneWeek);
+ $sum = 0;
+
+ for ($day = 0; $day < 7; ++$day) {
+ if ($days & pow(2, $day)) {
+ $sum += $x + (($w[0] > $w[1]) ? $w[0] <= $day || $day <= $w[1] : $w[0] <= $day && $day <= $w[1]);
+ }
+ }
+
+ return $sum;
+ }
+
+ /**
+ * Converts a negative day ordinal to
+ * its equivalent positive form
+ *
+ * @param integer $dayNumber
+ * @param integer $weekday
+ * @param integer $timestamp
+ * @return string
+ */
+ protected function convertDayOrdinalToPositive($dayNumber, $weekday, $timestamp)
+ {
+ $dayNumber = empty($dayNumber) ? 1 : $dayNumber; // Returns 0 when no number defined in BYDAY
+
+ $dayOrdinals = $this->dayOrdinals;
+
+ // We only care about negative BYDAY values
+ if ($dayNumber >= 1) {
+ return $dayOrdinals[$dayNumber];
+ }
+
+ $timestamp = (is_object($timestamp)) ? $timestamp : \DateTime::createFromFormat(self::UNIX_FORMAT, $timestamp);
+ $start = strtotime('first day of ' . $timestamp->format(self::DATE_TIME_FORMAT_PRETTY));
+ $end = strtotime('last day of ' . $timestamp->format(self::DATE_TIME_FORMAT_PRETTY));
+
+ // Used with pow(2, X) so pow(2, 4) is THURSDAY
+ $weekdays = array_flip(array_keys($this->weekdays));
+
+ $numberOfDays = $this->numberOfDays(pow(2, $weekdays[$weekday]), $start, $end);
+
+ // Create subset
+ $dayOrdinals = array_slice($dayOrdinals, 0, $numberOfDays, true);
+
+ // Reverse only the values
+ $dayOrdinals = array_combine(array_keys($dayOrdinals), array_reverse(array_values($dayOrdinals)));
+
+ return $dayOrdinals[$dayNumber * -1];
+ }
+
+ /**
+ * Removes unprintable ASCII and UTF-8 characters
+ *
+ * @param string $data
+ * @return string
+ */
+ protected function removeUnprintableChars($data)
+ {
+ return preg_replace('/[\x00-\x1F\x7F\xA0]/u', '', $data);
+ }
+
+ /**
+ * Provides a polyfill for PHP 7.2's `mb_chr()`, which is a multibyte safe version of `chr()`.
+ * Multibyte safe.
+ *
+ * @param integer $code
+ * @return string
+ */
+ protected function mb_chr($code)
+ {
+ if (function_exists('mb_chr')) {
+ return mb_chr($code);
+ } else {
+ if (0x80 > $code %= 0x200000) {
+ $s = chr($code);
+ } elseif (0x800 > $code) {
+ $s = chr(0xc0 | $code >> 6) . chr(0x80 | $code & 0x3f);
+ } elseif (0x10000 > $code) {
+ $s = chr(0xe0 | $code >> 12) . chr(0x80 | $code >> 6 & 0x3f) . chr(0x80 | $code & 0x3f);
+ } else {
+ $s = chr(0xf0 | $code >> 18) . chr(0x80 | $code >> 12 & 0x3f) . chr(0x80 | $code >> 6 & 0x3f) . chr(0x80 | $code & 0x3f);
+ }
+
+ return $s;
+ }
+ }
+
+ /**
+ * Replaces all occurrences of a search string with a given replacement string.
+ * Multibyte safe.
+ *
+ * @param string|array $search The value being searched for, otherwise known as the needle. An array may be used to designate multiple needles.
+ * @param string|array $replace The replacement value that replaces found search values. An array may be used to designate multiple replacements.
+ * @param string|array $subject The string or array being searched and replaced on, otherwise known as the haystack.
+ * If subject is an array, then the search and replace is performed with every entry of subject, and the return value is an array as well.
+ * @param integer $count If passed, this will be set to the number of replacements performed.
+ * @return array|string
+ */
+ protected function mb_str_replace($search, $replace, $subject, &$count = 0)
+ {
+ if (!is_array($subject)) {
+ // Normalize `$search` and `$replace` so they are both arrays of the same length
+ $searches = is_array($search) ? array_values($search) : array($search);
+ $replacements = is_array($replace) ? array_values($replace) : array($replace);
+ $replacements = array_pad($replacements, count($searches), '');
+
+ foreach ($searches as $key => $search) {
+ $parts = mb_split(preg_quote($search), $subject);
+ $count += count($parts) - 1;
+ $subject = implode($replacements[$key], $parts);
+ }
+ } else {
+ // Call `mb_str_replace` for each subject in array, recursively
+ foreach ($subject as $key => $value) {
+ $subject[$key] = $this->mb_str_replace($search, $replace, $value, $count);
+ }
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Replaces curly quotes and other special characters
+ * with their standard equivalents
+ *
+ * @param string $data
+ * @return string
+ */
+ protected function cleanData($data)
+ {
+ $replacementChars = array(
+ "\xe2\x80\x98" => "'", // ‘
+ "\xe2\x80\x99" => "'", // ’
+ "\xe2\x80\x9a" => "'", // ‚
+ "\xe2\x80\x9b" => "'", // ‛
+ "\xe2\x80\x9c" => '"', // “
+ "\xe2\x80\x9d" => '"', // ”
+ "\xe2\x80\x9e" => '"', // „
+ "\xe2\x80\x9f" => '"', // ‟
+ "\xe2\x80\x93" => '-', // –
+ "\xe2\x80\x94" => '--', // —
+ "\xe2\x80\xa6" => '...', // …
+ "\xc2\xa0" => ' ',
+ );
+ // Replace UTF-8 characters
+ $cleanedData = strtr($data, $replacementChars);
+
+ // Replace Windows-1252 equivalents
+ $charsToReplace = array_map(function ($code) {
+ return $this->mb_chr($code);
+ }, array(133, 145, 146, 147, 148, 150, 151, 194));
+ $cleanedData = $this->mb_str_replace($charsToReplace, $replacementChars, $cleanedData);
+
+ return $cleanedData;
+ }
+
+ /**
+ * Parses a list of excluded dates
+ * to be applied to an Event
+ *
+ * @param array $event
+ * @return array
+ */
+ public function parseExdates(array $event)
+ {
+ if (empty($event['EXDATE_array'])) {
+ return array();
+ } else {
+ $exdates = $event['EXDATE_array'];
+ }
+
+ $output = array();
+ $currentTimeZone = $this->defaultTimeZone;
+
+ foreach ($exdates as $subArray) {
+ end($subArray);
+ $finalKey = key($subArray);
+
+ foreach ($subArray as $key => $value) {
+ if ($key === 'TZID') {
+ $currentTimeZone = $subArray[$key];
+ } elseif (is_numeric($key)) {
+ $icalDate = $subArray[$key];
+
+ if (substr($icalDate, -1) === 'Z') {
+ $currentTimeZone = self::TIME_ZONE_UTC;
+ }
+
+ $output[] = new Carbon($icalDate, $currentTimeZone);
+
+ if ($key === $finalKey) {
+ // Reset to default
+ $currentTimeZone = $this->defaultTimeZone;
+ }
+ }
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Checks if a date string is a valid date
+ *
+ * @param string $value
+ * @return boolean
+ * @throws Exception
+ */
+ public function isValidDate($value)
+ {
+ if (!$value) {
+ return false;
+ }
+
+ try {
+ new \DateTime($value);
+
+ return true;
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if a filename exists as a file or URL
+ *
+ * @param string $filename
+ * @return boolean
+ */
+ protected function isFileOrUrl($filename)
+ {
+ return (file_exists($filename) || filter_var($filename, FILTER_VALIDATE_URL)) ?: false;
+ }
+
+ /**
+ * Reads an entire file or URL into an array
+ *
+ * @param string $filename
+ * @return array
+ * @throws Exception
+ */
+ protected function fileOrUrl($filename)
+ {
+ if (!$lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)) {
+ throw new \Exception("The file path or URL '{$filename}' does not exist.");
+ }
+
+ return $lines;
+ }
+
+ /**
+ * Ensures the recurrence count is enforced against generated recurrence events.
+ *
+ * @param array $rrules
+ * @param array $recurrenceEvents
+ * @return array
+ */
+ protected function trimToRecurrenceCount(array $rrules, array $recurrenceEvents)
+ {
+ if (isset($rrules['COUNT'])) {
+ $recurrenceCount = (intval($rrules['COUNT']) - 1);
+ $surplusCount = (sizeof($recurrenceEvents) - $recurrenceCount);
+
+ if ($surplusCount > 0) {
+ $recurrenceEvents = array_slice($recurrenceEvents, 0, $recurrenceCount);
+ $this->eventCount -= $surplusCount;
+ }
+ }
+
+ return $recurrenceEvents;
+ }
+
+ /**
+ * Checks if an excluded date matches a given date by reconciling time zones.
+ *
+ * @param integer $exdate
+ * @param array $anEvent
+ * @param integer $recurringOffset
+ * @return boolean
+ */
+ protected function isExdateMatch($exdate, array $anEvent, $recurringOffset)
+ {
+ $searchDate = $anEvent['DTSTART'];
+
+ if (substr($searchDate, -1) === 'Z') {
+ $timeZone = self::TIME_ZONE_UTC;
+ } elseif (isset($anEvent['DTSTART_array'][0]['TZID'])) {
+ $timeZone = $anEvent['DTSTART_array'][0]['TZID'];
+ } else {
+ $timeZone = $this->defaultTimeZone;
+ }
+
+ $a = new Carbon($searchDate, $timeZone);
+ $b = $exdate->addSeconds($recurringOffset);
+
+ return $a->eq($b);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer' . '/autoload_real.php';
+
+return ComposerAutoloaderInit6eb1bc205140b4de7b5f98e4a67572a4::getLoader();
--- /dev/null
+../squizlabs/php_codesniffer/bin/phpcbf
\ No newline at end of file
--- /dev/null
+../squizlabs/php_codesniffer/bin/phpcs
\ No newline at end of file
--- /dev/null
+<?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;
+
+ 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;
+ }
+
+ /**
+ * 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)
+ {
+ // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
+ if ('\\' == $class[0]) {
+ $class = substr($class, 1);
+ }
+
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative) {
+ return false;
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if ($file === null && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if ($file === null) {
+ // Remember that this class does not exist.
+ return $this->classMap[$class] = false;
+ }
+
+ return $file;
+ }
+
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+ 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;
+ }
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+ include $file;
+}
--- /dev/null
+
+Copyright (c) 2015 Nils Adermann, Jordi Boggiano
+
+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.
+
--- /dev/null
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);
--- /dev/null
+<?php
+
+// autoload_files.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+ '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
+);
--- /dev/null
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+ 'ICal' => array($baseDir . '/src'),
+);
--- /dev/null
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+ 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
+ 'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
+ 'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
+);
--- /dev/null
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInit6eb1bc205140b4de7b5f98e4a67572a4
+{
+ 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('ComposerAutoloaderInit6eb1bc205140b4de7b5f98e4a67572a4', 'loadClassLoader'), true, true);
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
+ spl_autoload_unregister(array('ComposerAutoloaderInit6eb1bc205140b4de7b5f98e4a67572a4', 'loadClassLoader'));
+
+ $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);
+
+ $includeFiles = require __DIR__ . '/autoload_files.php';
+ foreach ($includeFiles as $fileIdentifier => $file) {
+ composerRequire6eb1bc205140b4de7b5f98e4a67572a4($fileIdentifier, $file);
+ }
+
+ return $loader;
+ }
+}
+
+function composerRequire6eb1bc205140b4de7b5f98e4a67572a4($fileIdentifier, $file)
+{
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+ require $file;
+
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+ }
+}
--- /dev/null
+[
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.6.0",
+ "version_normalized": "1.6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
+ "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "time": "2017-10-11 12:05:26",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ]
+ },
+ {
+ "name": "symfony/translation",
+ "version": "v3.3.10",
+ "version_normalized": "3.3.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation.git",
+ "reference": "409bf229cd552bf7e3faa8ab7e3980b07672073f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/409bf229cd552bf7e3faa8ab7e3980b07672073f",
+ "reference": "409bf229cd552bf7e3faa8ab7e3980b07672073f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.5.9|>=7.0.8",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/config": "<2.8",
+ "symfony/yaml": "<3.3"
+ },
+ "require-dev": {
+ "psr/log": "~1.0",
+ "symfony/config": "~2.8|~3.0",
+ "symfony/intl": "^2.8.18|^3.2.5",
+ "symfony/yaml": "~3.3"
+ },
+ "suggest": {
+ "psr/log": "To use logging capability in translator",
+ "symfony/config": "",
+ "symfony/yaml": ""
+ },
+ "time": "2017-10-02 06:42:24",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.3-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Translation Component",
+ "homepage": "https://symfony.com"
+ },
+ {
+ "name": "nesbot/carbon",
+ "version": "1.22.1",
+ "version_normalized": "1.22.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/briannesbitt/Carbon.git",
+ "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc",
+ "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "symfony/translation": "~2.6 || ~3.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "~2",
+ "phpunit/phpunit": "~4.0 || ~5.0"
+ },
+ "time": "2017-01-16 07:55:07",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.23-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Carbon\\": "src/Carbon/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "http://nesbot.com"
+ }
+ ],
+ "description": "A simple API extension for DateTime.",
+ "homepage": "http://carbon.nesbot.com",
+ "keywords": [
+ "date",
+ "datetime",
+ "time"
+ ]
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.1.1",
+ "version_normalized": "3.1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+ "reference": "d667e245d5dcd4d7bf80f26f2c947d476b66213e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d667e245d5dcd4d7bf80f26f2c947d476b66213e",
+ "reference": "d667e245d5dcd4d7bf80f26f2c947d476b66213e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0"
+ },
+ "time": "2017-10-16 22:40:25",
+ "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"
+ ]
+ }
+]
--- /dev/null
+<?php
+
+use PhpCsFixer\Config;
+use PhpCsFixer\Finder;
+
+$rules = [
+ '@PSR2' => true,
+ 'array_syntax' => [
+ 'syntax' => 'long',
+ ],
+ 'binary_operator_spaces' => [
+ 'align_double_arrow' => false,
+ 'align_equals' => false,
+ ],
+ 'blank_line_before_return' => true,
+ 'cast_spaces' => true,
+ 'concat_space' => [
+ 'spacing' => 'none',
+ ],
+ 'ereg_to_preg' => true,
+ 'method_separation' => true,
+ 'no_blank_lines_after_phpdoc' => true,
+ 'no_extra_consecutive_blank_lines' => true,
+ 'no_short_bool_cast' => true,
+ 'no_unneeded_control_parentheses' => true,
+ 'no_unused_imports' => true,
+ 'no_whitespace_in_blank_line' => true,
+ 'ordered_imports' => true,
+ 'phpdoc_align' => true,
+ 'phpdoc_indent' => true,
+ 'phpdoc_inline_tag' => true,
+ 'phpdoc_no_access' => true,
+ 'phpdoc_no_alias_tag' => [
+ 'type' => 'var',
+ ],
+ 'phpdoc_no_package' => true,
+ 'phpdoc_order' => true,
+ 'phpdoc_scalar' => true,
+ 'phpdoc_separation' => true,
+ 'phpdoc_to_comment' => true,
+ 'phpdoc_trim' => true,
+ 'phpdoc_types' => true,
+ 'phpdoc_var_without_name' => true,
+ 'self_accessor' => true,
+ 'single_quote' => true,
+ 'space_after_semicolon' => true,
+ 'standardize_not_equals' => true,
+ 'ternary_operator_spaces' => true,
+ 'trailing_comma_in_multiline_array' => true,
+ 'trim_array_spaces' => true,
+ 'unary_operator_spaces' => true,
+];
+
+return Config::create()->setRules($rules)
+ ->setFinder(Finder::create()->in(__DIR__))
+ ->setUsingCache(true)
+ ->setRiskyAllowed(true);
\ No newline at end of file
--- /dev/null
+Copyright (C) Brian Nesbitt
+
+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.
--- /dev/null
+{
+ "name": "nesbot/carbon",
+ "type": "library",
+ "description": "A simple API extension for DateTime.",
+ "keywords": [
+ "date",
+ "time",
+ "DateTime"
+ ],
+ "homepage": "http://carbon.nesbot.com",
+ "support": {
+ "issues": "https://github.com/briannesbitt/Carbon/issues",
+ "source": "https://github.com/briannesbitt/Carbon"
+ },
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "http://nesbot.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0",
+ "symfony/translation": "~2.6 || ~3.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "~2",
+ "phpunit/phpunit": "~4.0 || ~5.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Carbon\\": "src/Carbon/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Tests\\": "tests/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.23-dev"
+ }
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "scripts": {
+ "test": "./vendor/bin/phpunit; ./vendor/bin/php-cs-fixer fix -v --diff --dry-run;",
+ "phpunit": "./vendor/bin/phpunit;",
+ "phpcs": "./vendor/bin/php-cs-fixer fix -v --diff --dry-run;"
+ }
+}
--- /dev/null
+# Carbon
+
+[](https://packagist.org/packages/nesbot/carbon)
+[](https://packagist.org/packages/nesbot/carbon)
+[](https://travis-ci.org/briannesbitt/Carbon)
+[](https://styleci.io/repos/5724990)
+[](https://codecov.io/github/briannesbitt/Carbon?branch=master)
+[](https://php-eye.com/package/nesbot/carbon)
+
+A simple PHP API extension for DateTime. [http://carbon.nesbot.com](http://carbon.nesbot.com)
+
+```php
+use Carbon\Carbon;
+
+printf("Right now is %s", Carbon::now()->toDateTimeString());
+printf("Right now in Vancouver is %s", Carbon::now('America/Vancouver')); //implicit __toString()
+$tomorrow = Carbon::now()->addDay();
+$lastWeek = Carbon::now()->subWeek();
+$nextSummerOlympics = Carbon::createFromDate(2012)->addYears(4);
+
+$officialDate = Carbon::now()->toRfc2822String();
+
+$howOldAmI = Carbon::createFromDate(1975, 5, 21)->age;
+
+$noonTodayLondonTime = Carbon::createFromTime(12, 0, 0, 'Europe/London');
+
+$worldWillEnd = Carbon::createFromDate(2012, 12, 21, 'GMT');
+
+// Don't really want to die so mock now
+Carbon::setTestNow(Carbon::createFromDate(2000, 1, 1));
+
+// comparisons are always done in UTC
+if (Carbon::now()->gte($worldWillEnd)) {
+ die();
+}
+
+// Phew! Return to normal behaviour
+Carbon::setTestNow();
+
+if (Carbon::now()->isWeekend()) {
+ echo 'Party!';
+}
+echo Carbon::now()->subMinutes(2)->diffForHumans(); // '2 minutes ago'
+
+// ... but also does 'from now', 'after' and 'before'
+// rolling up to seconds, minutes, hours, days, months, years
+
+$daysSinceEpoch = Carbon::createFromTimestamp(0)->diffInDays();
+```
+
+## Installation
+
+### With Composer
+
+```
+$ composer require nesbot/carbon
+```
+
+```json
+{
+ "require": {
+ "nesbot/carbon": "~1.21"
+ }
+}
+```
+
+```php
+<?php
+require 'vendor/autoload.php';
+
+use Carbon\Carbon;
+
+printf("Now: %s", Carbon::now());
+```
+
+<a name="install-nocomposer"/>
+### Without Composer
+
+Why are you not using [composer](http://getcomposer.org/)? Download [Carbon.php](https://github.com/briannesbitt/Carbon/blob/master/src/Carbon/Carbon.php) from the repo and save the file into your project path somewhere.
+
+```php
+<?php
+require 'path/to/Carbon.php';
+
+use Carbon\Carbon;
+
+printf("Now: %s", Carbon::now());
+```
+
+## Docs
+
+[http://carbon.nesbot.com/docs](http://carbon.nesbot.com/docs)
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Carbon\Exceptions\InvalidDateException;
+use Closure;
+use DatePeriod;
+use DateTime;
+use DateTimeZone;
+use InvalidArgumentException;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\Translator;
+use Symfony\Component\Translation\TranslatorInterface;
+
+/**
+ * A simple API extension for DateTime
+ *
+ * @property int $year
+ * @property int $yearIso
+ * @property int $month
+ * @property int $day
+ * @property int $hour
+ * @property int $minute
+ * @property int $second
+ * @property int $timestamp seconds since the Unix Epoch
+ * @property \DateTimeZone $timezone the current timezone
+ * @property \DateTimeZone $tz alias of timezone
+ * @property-read int $micro
+ * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday)
+ * @property-read int $dayOfYear 0 through 365
+ * @property-read int $weekOfMonth 1 through 5
+ * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday
+ * @property-read int $daysInMonth number of days in the given month
+ * @property-read int $age does a diffInYears() with default parameters
+ * @property-read int $quarter the quarter of this instance, 1 - 4
+ * @property-read int $offset the timezone offset in seconds from UTC
+ * @property-read int $offsetHours the timezone offset in hours from UTC
+ * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise
+ * @property-read bool $local checks if the timezone is local, true if local, false otherwise
+ * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise
+ * @property-read string $timezoneName
+ * @property-read string $tzName
+ */
+class Carbon extends DateTime
+{
+ /**
+ * The day constants.
+ */
+ const SUNDAY = 0;
+ const MONDAY = 1;
+ const TUESDAY = 2;
+ const WEDNESDAY = 3;
+ const THURSDAY = 4;
+ const FRIDAY = 5;
+ const SATURDAY = 6;
+
+ /**
+ * Names of days of the week.
+ *
+ * @var array
+ */
+ protected static $days = array(
+ self::SUNDAY => 'Sunday',
+ self::MONDAY => 'Monday',
+ self::TUESDAY => 'Tuesday',
+ self::WEDNESDAY => 'Wednesday',
+ self::THURSDAY => 'Thursday',
+ self::FRIDAY => 'Friday',
+ self::SATURDAY => 'Saturday',
+ );
+
+ /**
+ * Terms used to detect if a time passed is a relative date.
+ *
+ * This is here for testing purposes.
+ *
+ * @var array
+ */
+ protected static $relativeKeywords = array(
+ '+',
+ '-',
+ 'ago',
+ 'first',
+ 'last',
+ 'next',
+ 'this',
+ 'today',
+ 'tomorrow',
+ 'yesterday',
+ );
+
+ /**
+ * Number of X in Y.
+ */
+ const YEARS_PER_CENTURY = 100;
+ const YEARS_PER_DECADE = 10;
+ const MONTHS_PER_YEAR = 12;
+ const MONTHS_PER_QUARTER = 3;
+ const WEEKS_PER_YEAR = 52;
+ const DAYS_PER_WEEK = 7;
+ const HOURS_PER_DAY = 24;
+ const MINUTES_PER_HOUR = 60;
+ const SECONDS_PER_MINUTE = 60;
+
+ /**
+ * Default format to use for __toString method when type juggling occurs.
+ *
+ * @var string
+ */
+ const DEFAULT_TO_STRING_FORMAT = 'Y-m-d H:i:s';
+
+ /**
+ * Format to use for __toString method when type juggling occurs.
+ *
+ * @var string
+ */
+ protected static $toStringFormat = self::DEFAULT_TO_STRING_FORMAT;
+
+ /**
+ * First day of week.
+ *
+ * @var int
+ */
+ protected static $weekStartsAt = self::MONDAY;
+
+ /**
+ * Last day of week.
+ *
+ * @var int
+ */
+ protected static $weekEndsAt = self::SUNDAY;
+
+ /**
+ * Days of weekend.
+ *
+ * @var array
+ */
+ protected static $weekendDays = array(
+ self::SATURDAY,
+ self::SUNDAY,
+ );
+
+ /**
+ * A test Carbon instance to be returned when now instances are created.
+ *
+ * @var \Carbon\Carbon
+ */
+ protected static $testNow;
+
+ /**
+ * A translator to ... er ... translate stuff.
+ *
+ * @var \Symfony\Component\Translation\TranslatorInterface
+ */
+ protected static $translator;
+
+ /**
+ * The errors that can occur.
+ *
+ * @var array
+ */
+ protected static $lastErrors;
+
+ /**
+ * Will UTF8 encoding be used to print localized date/time ?
+ *
+ * @var bool
+ */
+ protected static $utf8 = false;
+
+ /*
+ * Indicates if months should be calculated with overflow.
+ *
+ * @var bool
+ */
+ protected static $monthsOverflow = true;
+
+ /**
+ * Indicates if months should be calculated with overflow.
+ *
+ * @param bool $monthsOverflow
+ *
+ * @return void
+ */
+ public static function useMonthsOverflow($monthsOverflow = true)
+ {
+ static::$monthsOverflow = $monthsOverflow;
+ }
+
+ /**
+ * Reset the month overflow behavior.
+ *
+ * @return void
+ */
+ public static function resetMonthsOverflow()
+ {
+ static::$monthsOverflow = true;
+ }
+
+ /**
+ * Get the month overflow behavior.
+ *
+ * @return bool
+ */
+ public static function shouldOverflowMonths()
+ {
+ return static::$monthsOverflow;
+ }
+
+ /**
+ * Creates a DateTimeZone from a string, DateTimeZone or integer offset.
+ *
+ * @param \DateTimeZone|string|int|null $object
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return \DateTimeZone
+ */
+ protected static function safeCreateDateTimeZone($object)
+ {
+ if ($object === null) {
+ // Don't return null... avoid Bug #52063 in PHP <5.3.6
+ return new DateTimeZone(date_default_timezone_get());
+ }
+
+ if ($object instanceof DateTimeZone) {
+ return $object;
+ }
+
+ if (is_numeric($object)) {
+ $tzName = timezone_name_from_abbr(null, $object * 3600, true);
+
+ if ($tzName === false) {
+ throw new InvalidArgumentException('Unknown or bad timezone ('.$object.')');
+ }
+
+ $object = $tzName;
+ }
+
+ $tz = @timezone_open((string) $object);
+
+ if ($tz === false) {
+ throw new InvalidArgumentException('Unknown or bad timezone ('.$object.')');
+ }
+
+ return $tz;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //////////////////////////// CONSTRUCTORS /////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Create a new Carbon instance.
+ *
+ * Please see the testing aids section (specifically static::setTestNow())
+ * for more on the possibility of this constructor returning a test instance.
+ *
+ * @param string|null $time
+ * @param \DateTimeZone|string|null $tz
+ */
+ public function __construct($time = null, $tz = null)
+ {
+ // If the class has a test now set and we are trying to create a now()
+ // instance then override as required
+ if (static::hasTestNow() && (empty($time) || $time === 'now' || static::hasRelativeKeywords($time))) {
+ $testInstance = clone static::getTestNow();
+ if (static::hasRelativeKeywords($time)) {
+ $testInstance->modify($time);
+ }
+
+ //shift the time according to the given time zone
+ if ($tz !== null && $tz !== static::getTestNow()->getTimezone()) {
+ $testInstance->setTimezone($tz);
+ } else {
+ $tz = $testInstance->getTimezone();
+ }
+
+ $time = $testInstance->toDateTimeString();
+ }
+
+ parent::__construct($time, static::safeCreateDateTimeZone($tz));
+ }
+
+ /**
+ * Create a Carbon instance from a DateTime one.
+ *
+ * @param \DateTime $dt
+ *
+ * @return static
+ */
+ public static function instance(DateTime $dt)
+ {
+ if ($dt instanceof static) {
+ return clone $dt;
+ }
+
+ return new static($dt->format('Y-m-d H:i:s.u'), $dt->getTimezone());
+ }
+
+ /**
+ * Create a carbon instance from a string.
+ *
+ * This is an alias for the constructor that allows better fluent syntax
+ * as it allows you to do Carbon::parse('Monday next week')->fn() rather
+ * than (new Carbon('Monday next week'))->fn().
+ *
+ * @param string|null $time
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function parse($time = null, $tz = null)
+ {
+ return new static($time, $tz);
+ }
+
+ /**
+ * Get a Carbon instance for the current date and time.
+ *
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function now($tz = null)
+ {
+ return new static(null, $tz);
+ }
+
+ /**
+ * Create a Carbon instance for today.
+ *
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function today($tz = null)
+ {
+ return static::now($tz)->startOfDay();
+ }
+
+ /**
+ * Create a Carbon instance for tomorrow.
+ *
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function tomorrow($tz = null)
+ {
+ return static::today($tz)->addDay();
+ }
+
+ /**
+ * Create a Carbon instance for yesterday.
+ *
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function yesterday($tz = null)
+ {
+ return static::today($tz)->subDay();
+ }
+
+ /**
+ * Create a Carbon instance for the greatest supported date.
+ *
+ * @return static
+ */
+ public static function maxValue()
+ {
+ if (PHP_INT_SIZE === 4) {
+ // 32 bit (and additionally Windows 64 bit)
+ return static::createFromTimestamp(PHP_INT_MAX);
+ }
+
+ // 64 bit
+ return static::create(9999, 12, 31, 23, 59, 59);
+ }
+
+ /**
+ * Create a Carbon instance for the lowest supported date.
+ *
+ * @return static
+ */
+ public static function minValue()
+ {
+ if (PHP_INT_SIZE === 4) {
+ // 32 bit (and additionally Windows 64 bit)
+ return static::createFromTimestamp(~PHP_INT_MAX);
+ }
+
+ // 64 bit
+ return static::create(1, 1, 1, 0, 0, 0);
+ }
+
+ /**
+ * Create a new Carbon instance from a specific date and time.
+ *
+ * If any of $year, $month or $day are set to null their now() values will
+ * be used.
+ *
+ * If $hour is null it will be set to its now() value and the default
+ * values for $minute and $second will be their now() values.
+ *
+ * If $hour is not null then the default values for $minute and $second
+ * will be 0.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function create($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null)
+ {
+ $now = static::hasTestNow() ? static::getTestNow()->getTimestamp() : time();
+
+ $defaults = array_combine(array(
+ 'year',
+ 'month',
+ 'day',
+ 'hour',
+ 'minute',
+ 'second',
+ ), explode('-', date('Y-n-j-G-i-s', $now)));
+
+ $year = $year === null ? $defaults['year'] : $year;
+ $month = $month === null ? $defaults['month'] : $month;
+ $day = $day === null ? $defaults['day'] : $day;
+
+ if ($hour === null) {
+ $hour = $defaults['hour'];
+ $minute = $minute === null ? $defaults['minute'] : $minute;
+ $second = $second === null ? $defaults['second'] : $second;
+ } else {
+ $minute = $minute === null ? 0 : $minute;
+ $second = $second === null ? 0 : $second;
+ }
+
+ $fixYear = null;
+
+ if ($year < 0) {
+ $fixYear = $year;
+ $year = 0;
+ } elseif ($year > 9999) {
+ $fixYear = $year - 9999;
+ $year = 9999;
+ }
+
+ $instance = static::createFromFormat('Y-n-j G:i:s', sprintf('%s-%s-%s %s:%02s:%02s', $year, $month, $day, $hour, $minute, $second), $tz);
+
+ if ($fixYear !== null) {
+ $instance->addYears($fixYear);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Create a new safe Carbon instance from a specific date and time.
+ *
+ * If any of $year, $month or $day are set to null their now() values will
+ * be used.
+ *
+ * If $hour is null it will be set to its now() value and the default
+ * values for $minute and $second will be their now() values.
+ *
+ * If $hour is not null then the default values for $minute and $second
+ * will be 0.
+ *
+ * If one of the set values is not valid, an \InvalidArgumentException
+ * will be thrown.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @throws \Carbon\Exceptions\InvalidDateException
+ *
+ * @return static
+ */
+ public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null)
+ {
+ $fields = array(
+ 'year' => array(0, 9999),
+ 'month' => array(0, 12),
+ 'day' => array(0, 31),
+ 'hour' => array(0, 24),
+ 'minute' => array(0, 59),
+ 'second' => array(0, 59),
+ );
+
+ foreach ($fields as $field => $range) {
+ if ($$field !== null && (!is_int($$field) || $$field < $range[0] || $$field > $range[1])) {
+ throw new InvalidDateException($field, $$field);
+ }
+ }
+
+ $instance = static::create($year, $month, 1, $hour, $minute, $second, $tz);
+
+ if ($day !== null && $day > $instance->daysInMonth) {
+ throw new InvalidDateException('day', $day);
+ }
+
+ return $instance->day($day);
+ }
+
+ /**
+ * Create a Carbon instance from just a date. The time portion is set to now.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function createFromDate($year = null, $month = null, $day = null, $tz = null)
+ {
+ return static::create($year, $month, $day, null, null, null, $tz);
+ }
+
+ /**
+ * Create a Carbon instance from just a time. The date portion is set to today.
+ *
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function createFromTime($hour = null, $minute = null, $second = null, $tz = null)
+ {
+ return static::create(null, null, null, $hour, $minute, $second, $tz);
+ }
+
+ /**
+ * Create a Carbon instance from a specific format.
+ *
+ * @param string $format
+ * @param string $time
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return static
+ */
+ public static function createFromFormat($format, $time, $tz = null)
+ {
+ if ($tz !== null) {
+ $dt = parent::createFromFormat($format, $time, static::safeCreateDateTimeZone($tz));
+ } else {
+ $dt = parent::createFromFormat($format, $time);
+ }
+
+ static::setLastErrors($lastErrors = parent::getLastErrors());
+
+ if ($dt instanceof DateTime) {
+ return static::instance($dt);
+ }
+
+ throw new InvalidArgumentException(implode(PHP_EOL, $lastErrors['errors']));
+ }
+
+ /**
+ * Set last errors.
+ *
+ * @param array $lastErrors
+ *
+ * @return void
+ */
+ private static function setLastErrors(array $lastErrors)
+ {
+ static::$lastErrors = $lastErrors;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getLastErrors()
+ {
+ return static::$lastErrors;
+ }
+
+ /**
+ * Create a Carbon instance from a timestamp.
+ *
+ * @param int $timestamp
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function createFromTimestamp($timestamp, $tz = null)
+ {
+ return static::now($tz)->setTimestamp($timestamp);
+ }
+
+ /**
+ * Create a Carbon instance from an UTC timestamp.
+ *
+ * @param int $timestamp
+ *
+ * @return static
+ */
+ public static function createFromTimestampUTC($timestamp)
+ {
+ return new static('@'.$timestamp);
+ }
+
+ /**
+ * Get a copy of the instance.
+ *
+ * @return static
+ */
+ public function copy()
+ {
+ return clone $this;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ ///////////////////////// GETTERS AND SETTERS /////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Get a part of the Carbon object
+ *
+ * @param string $name
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return string|int|\DateTimeZone
+ */
+ public function __get($name)
+ {
+ switch (true) {
+ case array_key_exists($name, $formats = array(
+ 'year' => 'Y',
+ 'yearIso' => 'o',
+ 'month' => 'n',
+ 'day' => 'j',
+ 'hour' => 'G',
+ 'minute' => 'i',
+ 'second' => 's',
+ 'micro' => 'u',
+ 'dayOfWeek' => 'w',
+ 'dayOfYear' => 'z',
+ 'weekOfYear' => 'W',
+ 'daysInMonth' => 't',
+ 'timestamp' => 'U',
+ )):
+ return (int) $this->format($formats[$name]);
+
+ case $name === 'weekOfMonth':
+ return (int) ceil($this->day / static::DAYS_PER_WEEK);
+
+ case $name === 'age':
+ return $this->diffInYears();
+
+ case $name === 'quarter':
+ return (int) ceil($this->month / static::MONTHS_PER_QUARTER);
+
+ case $name === 'offset':
+ return $this->getOffset();
+
+ case $name === 'offsetHours':
+ return $this->getOffset() / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR;
+
+ case $name === 'dst':
+ return $this->format('I') === '1';
+
+ case $name === 'local':
+ return $this->getOffset() === $this->copy()->setTimezone(date_default_timezone_get())->getOffset();
+
+ case $name === 'utc':
+ return $this->getOffset() === 0;
+
+ case $name === 'timezone' || $name === 'tz':
+ return $this->getTimezone();
+
+ case $name === 'timezoneName' || $name === 'tzName':
+ return $this->getTimezone()->getName();
+
+ default:
+ throw new InvalidArgumentException(sprintf("Unknown getter '%s'", $name));
+ }
+ }
+
+ /**
+ * Check if an attribute exists on the object
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function __isset($name)
+ {
+ try {
+ $this->__get($name);
+ } catch (InvalidArgumentException $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Set a part of the Carbon object
+ *
+ * @param string $name
+ * @param string|int|\DateTimeZone $value
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function __set($name, $value)
+ {
+ switch ($name) {
+ case 'year':
+ case 'month':
+ case 'day':
+ case 'hour':
+ case 'minute':
+ case 'second':
+ list($year, $month, $day, $hour, $minute, $second) = explode('-', $this->format('Y-n-j-G-i-s'));
+ $$name = $value;
+ $this->setDateTime($year, $month, $day, $hour, $minute, $second);
+ break;
+
+ case 'timestamp':
+ parent::setTimestamp($value);
+ break;
+
+ case 'timezone':
+ case 'tz':
+ $this->setTimezone($value);
+ break;
+
+ default:
+ throw new InvalidArgumentException(sprintf("Unknown setter '%s'", $name));
+ }
+ }
+
+ /**
+ * Set the instance's year
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function year($value)
+ {
+ $this->year = $value;
+
+ return $this;
+ }
+
+ /**
+ * Set the instance's month
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function month($value)
+ {
+ $this->month = $value;
+
+ return $this;
+ }
+
+ /**
+ * Set the instance's day
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function day($value)
+ {
+ $this->day = $value;
+
+ return $this;
+ }
+
+ /**
+ * Set the instance's hour
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function hour($value)
+ {
+ $this->hour = $value;
+
+ return $this;
+ }
+
+ /**
+ * Set the instance's minute
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function minute($value)
+ {
+ $this->minute = $value;
+
+ return $this;
+ }
+
+ /**
+ * Set the instance's second
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function second($value)
+ {
+ $this->second = $value;
+
+ return $this;
+ }
+
+ /**
+ * Sets the current date of the DateTime object to a different date.
+ * Calls modify as a workaround for a php bug
+ *
+ * @param int $year
+ * @param int $month
+ * @param int $day
+ *
+ * @return static
+ *
+ * @see https://github.com/briannesbitt/Carbon/issues/539
+ * @see https://bugs.php.net/bug.php?id=63863
+ */
+ public function setDate($year, $month, $day)
+ {
+ $this->modify('+0 day');
+
+ return parent::setDate($year, $month, $day);
+ }
+
+ /**
+ * Set the date and time all together
+ *
+ * @param int $year
+ * @param int $month
+ * @param int $day
+ * @param int $hour
+ * @param int $minute
+ * @param int $second
+ *
+ * @return static
+ */
+ public function setDateTime($year, $month, $day, $hour, $minute, $second = 0)
+ {
+ return $this->setDate($year, $month, $day)->setTime($hour, $minute, $second);
+ }
+
+ /**
+ * Set the time by time string
+ *
+ * @param string $time
+ *
+ * @return static
+ */
+ public function setTimeFromTimeString($time)
+ {
+ $time = explode(':', $time);
+
+ $hour = $time[0];
+ $minute = isset($time[1]) ? $time[1] : 0;
+ $second = isset($time[2]) ? $time[2] : 0;
+
+ return $this->setTime($hour, $minute, $second);
+ }
+
+ /**
+ * Set the instance's timestamp
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function timestamp($value)
+ {
+ return $this->setTimestamp($value);
+ }
+
+ /**
+ * Alias for setTimezone()
+ *
+ * @param \DateTimeZone|string $value
+ *
+ * @return static
+ */
+ public function timezone($value)
+ {
+ return $this->setTimezone($value);
+ }
+
+ /**
+ * Alias for setTimezone()
+ *
+ * @param \DateTimeZone|string $value
+ *
+ * @return static
+ */
+ public function tz($value)
+ {
+ return $this->setTimezone($value);
+ }
+
+ /**
+ * Set the instance's timezone from a string or object
+ *
+ * @param \DateTimeZone|string $value
+ *
+ * @return static
+ */
+ public function setTimezone($value)
+ {
+ return parent::setTimezone(static::safeCreateDateTimeZone($value));
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////// WEEK SPECIAL DAYS /////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Get the first day of week
+ *
+ * @return int
+ */
+ public static function getWeekStartsAt()
+ {
+ return static::$weekStartsAt;
+ }
+
+ /**
+ * Set the first day of week
+ *
+ * @param int
+ */
+ public static function setWeekStartsAt($day)
+ {
+ static::$weekStartsAt = $day;
+ }
+
+ /**
+ * Get the last day of week
+ *
+ * @return int
+ */
+ public static function getWeekEndsAt()
+ {
+ return static::$weekEndsAt;
+ }
+
+ /**
+ * Set the last day of week
+ *
+ * @param int
+ */
+ public static function setWeekEndsAt($day)
+ {
+ static::$weekEndsAt = $day;
+ }
+
+ /**
+ * Get weekend days
+ *
+ * @return array
+ */
+ public static function getWeekendDays()
+ {
+ return static::$weekendDays;
+ }
+
+ /**
+ * Set weekend days
+ *
+ * @param array
+ */
+ public static function setWeekendDays($days)
+ {
+ static::$weekendDays = $days;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ ///////////////////////// TESTING AIDS ////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Set a Carbon instance (real or mock) to be returned when a "now"
+ * instance is created. The provided instance will be returned
+ * specifically under the following conditions:
+ * - A call to the static now() method, ex. Carbon::now()
+ * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null)
+ * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now')
+ * - When a string containing the desired time is passed to Carbon::parse().
+ *
+ * Note the timezone parameter was left out of the examples above and
+ * has no affect as the mock value will be returned regardless of its value.
+ *
+ * To clear the test instance call this method using the default
+ * parameter of null.
+ *
+ * @param \Carbon\Carbon|string|null $testNow
+ */
+ public static function setTestNow($testNow = null)
+ {
+ static::$testNow = is_string($testNow) ? static::parse($testNow) : $testNow;
+ }
+
+ /**
+ * Get the Carbon instance (real or mock) to be returned when a "now"
+ * instance is created.
+ *
+ * @return static the current instance used for testing
+ */
+ public static function getTestNow()
+ {
+ return static::$testNow;
+ }
+
+ /**
+ * Determine if there is a valid test instance set. A valid test instance
+ * is anything that is not null.
+ *
+ * @return bool true if there is a test instance, otherwise false
+ */
+ public static function hasTestNow()
+ {
+ return static::getTestNow() !== null;
+ }
+
+ /**
+ * Determine if there is a relative keyword in the time string, this is to
+ * create dates relative to now for test instances. e.g.: next tuesday
+ *
+ * @param string $time
+ *
+ * @return bool true if there is a keyword, otherwise false
+ */
+ public static function hasRelativeKeywords($time)
+ {
+ // skip common format with a '-' in it
+ if (preg_match('/\d{4}-\d{1,2}-\d{1,2}/', $time) !== 1) {
+ foreach (static::$relativeKeywords as $keyword) {
+ if (stripos($time, $keyword) !== false) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////// LOCALIZATION //////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Initialize the translator instance if necessary.
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ protected static function translator()
+ {
+ if (static::$translator === null) {
+ static::$translator = new Translator('en');
+ static::$translator->addLoader('array', new ArrayLoader());
+ static::setLocale('en');
+ }
+
+ return static::$translator;
+ }
+
+ /**
+ * Get the translator instance in use
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ public static function getTranslator()
+ {
+ return static::translator();
+ }
+
+ /**
+ * Set the translator instance to use
+ *
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator
+ */
+ public static function setTranslator(TranslatorInterface $translator)
+ {
+ static::$translator = $translator;
+ }
+
+ /**
+ * Get the current translator locale
+ *
+ * @return string
+ */
+ public static function getLocale()
+ {
+ return static::translator()->getLocale();
+ }
+
+ /**
+ * Set the current translator locale and indicate if the source locale file exists
+ *
+ * @param string $locale
+ *
+ * @return bool
+ */
+ public static function setLocale($locale)
+ {
+ $locale = preg_replace_callback('/\b([a-z]{2})[-_](?:([a-z]{4})[-_])?([a-z]{2})\b/', function ($matches) {
+ return $matches[1].'_'.(!empty($matches[2]) ? ucfirst($matches[2]).'_' : '').strtoupper($matches[3]);
+ }, strtolower($locale));
+
+ if (file_exists($filename = __DIR__.'/Lang/'.$locale.'.php')) {
+ static::translator()->setLocale($locale);
+ // Ensure the locale has been loaded.
+ static::translator()->addResource('array', require $filename, $locale);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////// STRING FORMATTING /////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Set if UTF8 will be used for localized date/time
+ *
+ * @param bool $utf8
+ */
+ public static function setUtf8($utf8)
+ {
+ static::$utf8 = $utf8;
+ }
+
+ /**
+ * Format the instance with the current locale. You can set the current
+ * locale using setlocale() http://php.net/setlocale.
+ *
+ * @param string $format
+ *
+ * @return string
+ */
+ public function formatLocalized($format)
+ {
+ // Check for Windows to find and replace the %e
+ // modifier correctly
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+ $format = preg_replace('#(?<!%)((?:%%)*)%e#', '\1%#d', $format);
+ }
+
+ $formatted = strftime($format, strtotime($this));
+
+ return static::$utf8 ? utf8_encode($formatted) : $formatted;
+ }
+
+ /**
+ * Reset the format used to the default when type juggling a Carbon instance to a string
+ */
+ public static function resetToStringFormat()
+ {
+ static::setToStringFormat(static::DEFAULT_TO_STRING_FORMAT);
+ }
+
+ /**
+ * Set the default format used when type juggling a Carbon instance to a string
+ *
+ * @param string $format
+ */
+ public static function setToStringFormat($format)
+ {
+ static::$toStringFormat = $format;
+ }
+
+ /**
+ * Format the instance as a string using the set format
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->format(static::$toStringFormat);
+ }
+
+ /**
+ * Format the instance as date
+ *
+ * @return string
+ */
+ public function toDateString()
+ {
+ return $this->format('Y-m-d');
+ }
+
+ /**
+ * Format the instance as a readable date
+ *
+ * @return string
+ */
+ public function toFormattedDateString()
+ {
+ return $this->format('M j, Y');
+ }
+
+ /**
+ * Format the instance as time
+ *
+ * @return string
+ */
+ public function toTimeString()
+ {
+ return $this->format('H:i:s');
+ }
+
+ /**
+ * Format the instance as date and time
+ *
+ * @return string
+ */
+ public function toDateTimeString()
+ {
+ return $this->format('Y-m-d H:i:s');
+ }
+
+ /**
+ * Format the instance with day, date and time
+ *
+ * @return string
+ */
+ public function toDayDateTimeString()
+ {
+ return $this->format('D, M j, Y g:i A');
+ }
+
+ /**
+ * Format the instance as ATOM
+ *
+ * @return string
+ */
+ public function toAtomString()
+ {
+ return $this->format(static::ATOM);
+ }
+
+ /**
+ * Format the instance as COOKIE
+ *
+ * @return string
+ */
+ public function toCookieString()
+ {
+ return $this->format(static::COOKIE);
+ }
+
+ /**
+ * Format the instance as ISO8601
+ *
+ * @return string
+ */
+ public function toIso8601String()
+ {
+ return $this->toAtomString();
+ }
+
+ /**
+ * Format the instance as RFC822
+ *
+ * @return string
+ */
+ public function toRfc822String()
+ {
+ return $this->format(static::RFC822);
+ }
+
+ /**
+ * Format the instance as RFC850
+ *
+ * @return string
+ */
+ public function toRfc850String()
+ {
+ return $this->format(static::RFC850);
+ }
+
+ /**
+ * Format the instance as RFC1036
+ *
+ * @return string
+ */
+ public function toRfc1036String()
+ {
+ return $this->format(static::RFC1036);
+ }
+
+ /**
+ * Format the instance as RFC1123
+ *
+ * @return string
+ */
+ public function toRfc1123String()
+ {
+ return $this->format(static::RFC1123);
+ }
+
+ /**
+ * Format the instance as RFC2822
+ *
+ * @return string
+ */
+ public function toRfc2822String()
+ {
+ return $this->format(static::RFC2822);
+ }
+
+ /**
+ * Format the instance as RFC3339
+ *
+ * @return string
+ */
+ public function toRfc3339String()
+ {
+ return $this->format(static::RFC3339);
+ }
+
+ /**
+ * Format the instance as RSS
+ *
+ * @return string
+ */
+ public function toRssString()
+ {
+ return $this->format(static::RSS);
+ }
+
+ /**
+ * Format the instance as W3C
+ *
+ * @return string
+ */
+ public function toW3cString()
+ {
+ return $this->format(static::W3C);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ ////////////////////////// COMPARISONS ////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Determines if the instance is equal to another
+ *
+ * @param Carbon $dt
+ *
+ * @return bool
+ */
+ public function eq(Carbon $dt)
+ {
+ return $this == $dt;
+ }
+
+ /**
+ * Determines if the instance is equal to another
+ *
+ * @param Carbon $dt
+ *
+ * @see eq()
+ *
+ * @return bool
+ */
+ public function equalTo(Carbon $dt)
+ {
+ return $this->eq($dt);
+ }
+
+ /**
+ * Determines if the instance is not equal to another
+ *
+ * @param Carbon $dt
+ *
+ * @return bool
+ */
+ public function ne(Carbon $dt)
+ {
+ return !$this->eq($dt);
+ }
+
+ /**
+ * Determines if the instance is not equal to another
+ *
+ * @param Carbon $dt
+ *
+ * @see ne()
+ *
+ * @return bool
+ */
+ public function notEqualTo(Carbon $dt)
+ {
+ return $this->ne($dt);
+ }
+
+ /**
+ * Determines if the instance is greater (after) than another
+ *
+ * @param Carbon $dt
+ *
+ * @return bool
+ */
+ public function gt(Carbon $dt)
+ {
+ return $this > $dt;
+ }
+
+ /**
+ * Determines if the instance is greater (after) than another
+ *
+ * @param Carbon $dt
+ *
+ * @see gt()
+ *
+ * @return bool
+ */
+ public function greaterThan(Carbon $dt)
+ {
+ return $this->gt($dt);
+ }
+
+ /**
+ * Determines if the instance is greater (after) than or equal to another
+ *
+ * @param Carbon $dt
+ *
+ * @return bool
+ */
+ public function gte(Carbon $dt)
+ {
+ return $this >= $dt;
+ }
+
+ /**
+ * Determines if the instance is greater (after) than or equal to another
+ *
+ * @param Carbon $dt
+ *
+ * @see gte()
+ *
+ * @return bool
+ */
+ public function greaterThanOrEqualTo(Carbon $dt)
+ {
+ return $this->gte($dt);
+ }
+
+ /**
+ * Determines if the instance is less (before) than another
+ *
+ * @param Carbon $dt
+ *
+ * @return bool
+ */
+ public function lt(Carbon $dt)
+ {
+ return $this < $dt;
+ }
+
+ /**
+ * Determines if the instance is less (before) than another
+ *
+ * @param Carbon $dt
+ *
+ * @see lt()
+ *
+ * @return bool
+ */
+ public function lessThan(Carbon $dt)
+ {
+ return $this->lt($dt);
+ }
+
+ /**
+ * Determines if the instance is less (before) or equal to another
+ *
+ * @param Carbon $dt
+ *
+ * @return bool
+ */
+ public function lte(Carbon $dt)
+ {
+ return $this <= $dt;
+ }
+
+ /**
+ * Determines if the instance is less (before) or equal to another
+ *
+ * @param Carbon $dt
+ *
+ * @see lte()
+ *
+ * @return bool
+ */
+ public function lessThanOrEqualTo(Carbon $dt)
+ {
+ return $this->lte($dt);
+ }
+
+ /**
+ * Determines if the instance is between two others
+ *
+ * @param Carbon $dt1
+ * @param Carbon $dt2
+ * @param bool $equal Indicates if a > and < comparison should be used or <= or >=
+ *
+ * @return bool
+ */
+ public function between(Carbon $dt1, Carbon $dt2, $equal = true)
+ {
+ if ($dt1->gt($dt2)) {
+ $temp = $dt1;
+ $dt1 = $dt2;
+ $dt2 = $temp;
+ }
+
+ if ($equal) {
+ return $this->gte($dt1) && $this->lte($dt2);
+ }
+
+ return $this->gt($dt1) && $this->lt($dt2);
+ }
+
+ /**
+ * Get the closest date from the instance.
+ *
+ * @param Carbon $dt1
+ * @param Carbon $dt2
+ *
+ * @return static
+ */
+ public function closest(Carbon $dt1, Carbon $dt2)
+ {
+ return $this->diffInSeconds($dt1) < $this->diffInSeconds($dt2) ? $dt1 : $dt2;
+ }
+
+ /**
+ * Get the farthest date from the instance.
+ *
+ * @param Carbon $dt1
+ * @param Carbon $dt2
+ *
+ * @return static
+ */
+ public function farthest(Carbon $dt1, Carbon $dt2)
+ {
+ return $this->diffInSeconds($dt1) > $this->diffInSeconds($dt2) ? $dt1 : $dt2;
+ }
+
+ /**
+ * Get the minimum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|null $dt
+ *
+ * @return static
+ */
+ public function min(Carbon $dt = null)
+ {
+ $dt = $dt ?: static::now($this->getTimezone());
+
+ return $this->lt($dt) ? $this : $dt;
+ }
+
+ /**
+ * Get the minimum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|null $dt
+ *
+ * @see min()
+ *
+ * @return static
+ */
+ public function minimum(Carbon $dt = null)
+ {
+ return $this->min($dt);
+ }
+
+ /**
+ * Get the maximum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|null $dt
+ *
+ * @return static
+ */
+ public function max(Carbon $dt = null)
+ {
+ $dt = $dt ?: static::now($this->getTimezone());
+
+ return $this->gt($dt) ? $this : $dt;
+ }
+
+ /**
+ * Get the maximum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|null $dt
+ *
+ * @see max()
+ *
+ * @return static
+ */
+ public function maximum(Carbon $dt = null)
+ {
+ return $this->max($dt);
+ }
+
+ /**
+ * Determines if the instance is a weekday
+ *
+ * @return bool
+ */
+ public function isWeekday()
+ {
+ return !$this->isWeekend();
+ }
+
+ /**
+ * Determines if the instance is a weekend day
+ *
+ * @return bool
+ */
+ public function isWeekend()
+ {
+ return in_array($this->dayOfWeek, static::$weekendDays);
+ }
+
+ /**
+ * Determines if the instance is yesterday
+ *
+ * @return bool
+ */
+ public function isYesterday()
+ {
+ return $this->toDateString() === static::yesterday($this->getTimezone())->toDateString();
+ }
+
+ /**
+ * Determines if the instance is today
+ *
+ * @return bool
+ */
+ public function isToday()
+ {
+ return $this->toDateString() === static::now($this->getTimezone())->toDateString();
+ }
+
+ /**
+ * Determines if the instance is tomorrow
+ *
+ * @return bool
+ */
+ public function isTomorrow()
+ {
+ return $this->toDateString() === static::tomorrow($this->getTimezone())->toDateString();
+ }
+
+ /**
+ * Determines if the instance is within the next week
+ *
+ * @return bool
+ */
+ public function isNextWeek()
+ {
+ return $this->weekOfYear === static::now($this->getTimezone())->addWeek()->weekOfYear;
+ }
+
+ /**
+ * Determines if the instance is within the last week
+ *
+ * @return bool
+ */
+ public function isLastWeek()
+ {
+ return $this->weekOfYear === static::now($this->getTimezone())->subWeek()->weekOfYear;
+ }
+
+ /**
+ * Determines if the instance is within the next month
+ *
+ * @return bool
+ */
+ public function isNextMonth()
+ {
+ return $this->month === static::now($this->getTimezone())->addMonthNoOverflow()->month;
+ }
+
+ /**
+ * Determines if the instance is within the last month
+ *
+ * @return bool
+ */
+ public function isLastMonth()
+ {
+ return $this->month === static::now($this->getTimezone())->subMonthNoOverflow()->month;
+ }
+
+ /**
+ * Determines if the instance is within next year
+ *
+ * @return bool
+ */
+ public function isNextYear()
+ {
+ return $this->year === static::now($this->getTimezone())->addYear()->year;
+ }
+
+ /**
+ * Determines if the instance is within the previous year
+ *
+ * @return bool
+ */
+ public function isLastYear()
+ {
+ return $this->year === static::now($this->getTimezone())->subYear()->year;
+ }
+
+ /**
+ * Determines if the instance is in the future, ie. greater (after) than now
+ *
+ * @return bool
+ */
+ public function isFuture()
+ {
+ return $this->gt(static::now($this->getTimezone()));
+ }
+
+ /**
+ * Determines if the instance is in the past, ie. less (before) than now
+ *
+ * @return bool
+ */
+ public function isPast()
+ {
+ return $this->lt(static::now($this->getTimezone()));
+ }
+
+ /**
+ * Determines if the instance is a leap year
+ *
+ * @return bool
+ */
+ public function isLeapYear()
+ {
+ return $this->format('L') === '1';
+ }
+
+ /**
+ * Determines if the instance is a long year
+ *
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates
+ *
+ * @return bool
+ */
+ public function isLongYear()
+ {
+ return static::create($this->year, 12, 28, 0, 0, 0, $this->tz)->weekOfYear === 53;
+ }
+
+ /*
+ * Compares the formatted values of the two dates.
+ *
+ * @param string $format The date formats to compare.
+ * @param \Carbon\Carbon|null $dt The instance to compare with or null to use current day.
+ *
+ * @return bool
+ */
+ public function isSameAs($format, Carbon $dt = null)
+ {
+ $dt = $dt ?: static::now($this->tz);
+
+ return $this->format($format) === $dt->format($format);
+ }
+
+ /**
+ * Determines if the instance is in the current year
+ *
+ * @return bool
+ */
+ public function isCurrentYear()
+ {
+ return $this->isSameYear();
+ }
+
+ /**
+ * Checks if the passed in date is in the same year as the instance year.
+ *
+ * @param \Carbon\Carbon|null $dt The instance to compare with or null to use current day.
+ *
+ * @return bool
+ */
+ public function isSameYear(Carbon $dt = null)
+ {
+ return $this->isSameAs('Y', $dt);
+ }
+
+ /**
+ * Determines if the instance is in the current month
+ *
+ * @return bool
+ */
+ public function isCurrentMonth()
+ {
+ return $this->isSameMonth();
+ }
+
+ /**
+ * Checks if the passed in date is in the same month as the instance month (and year if needed).
+ *
+ * @param \Carbon\Carbon|null $dt The instance to compare with or null to use current day.
+ * @param bool $ofSameYear Check if it is the same month in the same year.
+ *
+ * @return bool
+ */
+ public function isSameMonth(Carbon $dt = null, $ofSameYear = false)
+ {
+ $format = $ofSameYear ? 'Y-m' : 'm';
+
+ return $this->isSameAs($format, $dt);
+ }
+
+ /**
+ * Checks if the passed in date is the same day as the instance current day.
+ *
+ * @param \Carbon\Carbon $dt
+ *
+ * @return bool
+ */
+ public function isSameDay(Carbon $dt)
+ {
+ return $this->toDateString() === $dt->toDateString();
+ }
+
+ /**
+ * Checks if this day is a Sunday.
+ *
+ * @return bool
+ */
+ public function isSunday()
+ {
+ return $this->dayOfWeek === static::SUNDAY;
+ }
+
+ /**
+ * Checks if this day is a Monday.
+ *
+ * @return bool
+ */
+ public function isMonday()
+ {
+ return $this->dayOfWeek === static::MONDAY;
+ }
+
+ /**
+ * Checks if this day is a Tuesday.
+ *
+ * @return bool
+ */
+ public function isTuesday()
+ {
+ return $this->dayOfWeek === static::TUESDAY;
+ }
+
+ /**
+ * Checks if this day is a Wednesday.
+ *
+ * @return bool
+ */
+ public function isWednesday()
+ {
+ return $this->dayOfWeek === static::WEDNESDAY;
+ }
+
+ /**
+ * Checks if this day is a Thursday.
+ *
+ * @return bool
+ */
+ public function isThursday()
+ {
+ return $this->dayOfWeek === static::THURSDAY;
+ }
+
+ /**
+ * Checks if this day is a Friday.
+ *
+ * @return bool
+ */
+ public function isFriday()
+ {
+ return $this->dayOfWeek === static::FRIDAY;
+ }
+
+ /**
+ * Checks if this day is a Saturday.
+ *
+ * @return bool
+ */
+ public function isSaturday()
+ {
+ return $this->dayOfWeek === static::SATURDAY;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////// ADDITIONS AND SUBTRACTIONS ////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Add years to the instance. Positive $value travel forward while
+ * negative $value travel into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addYears($value)
+ {
+ return $this->modify((int) $value.' year');
+ }
+
+ /**
+ * Add a year to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addYear($value = 1)
+ {
+ return $this->addYears($value);
+ }
+
+ /**
+ * Remove a year from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subYear($value = 1)
+ {
+ return $this->subYears($value);
+ }
+
+ /**
+ * Remove years from the instance.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subYears($value)
+ {
+ return $this->addYears(-1 * $value);
+ }
+
+ /**
+ * Add quarters to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addQuarters($value)
+ {
+ return $this->addMonths(static::MONTHS_PER_QUARTER * $value);
+ }
+
+ /**
+ * Add a quarter to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addQuarter($value = 1)
+ {
+ return $this->addQuarters($value);
+ }
+
+ /**
+ * Remove a quarter from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subQuarter($value = 1)
+ {
+ return $this->subQuarters($value);
+ }
+
+ /**
+ * Remove quarters from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subQuarters($value)
+ {
+ return $this->addQuarters(-1 * $value);
+ }
+
+ /**
+ * Add centuries to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addCenturies($value)
+ {
+ return $this->addYears(static::YEARS_PER_CENTURY * $value);
+ }
+
+ /**
+ * Add a century to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addCentury($value = 1)
+ {
+ return $this->addCenturies($value);
+ }
+
+ /**
+ * Remove a century from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subCentury($value = 1)
+ {
+ return $this->subCenturies($value);
+ }
+
+ /**
+ * Remove centuries from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subCenturies($value)
+ {
+ return $this->addCenturies(-1 * $value);
+ }
+
+ /**
+ * Add months to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMonths($value)
+ {
+ if (static::shouldOverflowMonths()) {
+ return $this->addMonthsWithOverflow($value);
+ }
+
+ return $this->addMonthsNoOverflow($value);
+ }
+
+ /**
+ * Add a month to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMonth($value = 1)
+ {
+ return $this->addMonths($value);
+ }
+
+ /**
+ * Remove a month from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMonth($value = 1)
+ {
+ return $this->subMonths($value);
+ }
+
+ /**
+ * Remove months from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMonths($value)
+ {
+ return $this->addMonths(-1 * $value);
+ }
+
+ /**
+ * Add months to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMonthsWithOverflow($value)
+ {
+ return $this->modify((int) $value.' month');
+ }
+
+ /**
+ * Add a month to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMonthWithOverflow($value = 1)
+ {
+ return $this->addMonthsWithOverflow($value);
+ }
+
+ /**
+ * Remove a month from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMonthWithOverflow($value = 1)
+ {
+ return $this->subMonthsWithOverflow($value);
+ }
+
+ /**
+ * Remove months from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMonthsWithOverflow($value)
+ {
+ return $this->addMonthsWithOverflow(-1 * $value);
+ }
+
+ /**
+ * Add months without overflowing to the instance. Positive $value
+ * travels forward while negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMonthsNoOverflow($value)
+ {
+ $day = $this->day;
+
+ $this->modify((int) $value.' month');
+
+ if ($day !== $this->day) {
+ $this->modify('last day of previous month');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add a month with no overflow to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMonthNoOverflow($value = 1)
+ {
+ return $this->addMonthsNoOverflow($value);
+ }
+
+ /**
+ * Remove a month with no overflow from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMonthNoOverflow($value = 1)
+ {
+ return $this->subMonthsNoOverflow($value);
+ }
+
+ /**
+ * Remove months with no overflow from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMonthsNoOverflow($value)
+ {
+ return $this->addMonthsNoOverflow(-1 * $value);
+ }
+
+ /**
+ * Add days to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addDays($value)
+ {
+ return $this->modify((int) $value.' day');
+ }
+
+ /**
+ * Add a day to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addDay($value = 1)
+ {
+ return $this->addDays($value);
+ }
+
+ /**
+ * Remove a day from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subDay($value = 1)
+ {
+ return $this->subDays($value);
+ }
+
+ /**
+ * Remove days from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subDays($value)
+ {
+ return $this->addDays(-1 * $value);
+ }
+
+ /**
+ * Add weekdays to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addWeekdays($value)
+ {
+ // fix for https://bugs.php.net/bug.php?id=54909
+ $t = $this->toTimeString();
+ $this->modify((int) $value.' weekday');
+
+ return $this->setTimeFromTimeString($t);
+ }
+
+ /**
+ * Add a weekday to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addWeekday($value = 1)
+ {
+ return $this->addWeekdays($value);
+ }
+
+ /**
+ * Remove a weekday from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subWeekday($value = 1)
+ {
+ return $this->subWeekdays($value);
+ }
+
+ /**
+ * Remove weekdays from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subWeekdays($value)
+ {
+ return $this->addWeekdays(-1 * $value);
+ }
+
+ /**
+ * Add weeks to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addWeeks($value)
+ {
+ return $this->modify((int) $value.' week');
+ }
+
+ /**
+ * Add a week to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addWeek($value = 1)
+ {
+ return $this->addWeeks($value);
+ }
+
+ /**
+ * Remove a week from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subWeek($value = 1)
+ {
+ return $this->subWeeks($value);
+ }
+
+ /**
+ * Remove weeks to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subWeeks($value)
+ {
+ return $this->addWeeks(-1 * $value);
+ }
+
+ /**
+ * Add hours to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addHours($value)
+ {
+ return $this->modify((int) $value.' hour');
+ }
+
+ /**
+ * Add an hour to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addHour($value = 1)
+ {
+ return $this->addHours($value);
+ }
+
+ /**
+ * Remove an hour from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subHour($value = 1)
+ {
+ return $this->subHours($value);
+ }
+
+ /**
+ * Remove hours from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subHours($value)
+ {
+ return $this->addHours(-1 * $value);
+ }
+
+ /**
+ * Add minutes to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMinutes($value)
+ {
+ return $this->modify((int) $value.' minute');
+ }
+
+ /**
+ * Add a minute to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addMinute($value = 1)
+ {
+ return $this->addMinutes($value);
+ }
+
+ /**
+ * Remove a minute from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMinute($value = 1)
+ {
+ return $this->subMinutes($value);
+ }
+
+ /**
+ * Remove minutes from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subMinutes($value)
+ {
+ return $this->addMinutes(-1 * $value);
+ }
+
+ /**
+ * Add seconds to the instance. Positive $value travels forward while
+ * negative $value travels into the past.
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addSeconds($value)
+ {
+ return $this->modify((int) $value.' second');
+ }
+
+ /**
+ * Add a second to the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addSecond($value = 1)
+ {
+ return $this->addSeconds($value);
+ }
+
+ /**
+ * Remove a second from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subSecond($value = 1)
+ {
+ return $this->subSeconds($value);
+ }
+
+ /**
+ * Remove seconds from the instance
+ *
+ * @param int $value
+ *
+ * @return static
+ */
+ public function subSeconds($value)
+ {
+ return $this->addSeconds(-1 * $value);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////////// DIFFERENCES ///////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Get the difference in years
+ *
+ * @param \Carbon\Carbon|null $dt
+ * @param bool $abs Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInYears(Carbon $dt = null, $abs = true)
+ {
+ $dt = $dt ?: static::now($this->getTimezone());
+
+ return (int) $this->diff($dt, $abs)->format('%r%y');
+ }
+
+ /**
+ * Get the difference in months
+ *
+ * @param \Carbon\Carbon|null $dt
+ * @param bool $abs Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInMonths(Carbon $dt = null, $abs = true)
+ {
+ $dt = $dt ?: static::now($this->getTimezone());
+
+ return $this->diffInYears($dt, $abs) * static::MONTHS_PER_YEAR + (int) $this->diff($dt, $abs)->format('%r%m');
+ }
+
+ /**
+ * Get the difference in weeks
+ *
+ * @param \Carbon\Carbon|null $dt
+ * @param bool $abs Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInWeeks(Carbon $dt = null, $abs = true)
+ {
+ return (int) ($this->diffInDays($dt, $abs) / static::DAYS_PER_WEEK);
+ }
+
+ /**
+ * Get the difference in days
+ *
+ * @param \Carbon\Carbon|null $dt
+ * @param bool $abs Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInDays(Carbon $dt = null, $abs = true)
+ {
+ $dt = $dt ?: static::now($this->getTimezone());
+
+ return (int) $this->diff($dt, $abs)->format('%r%a');
+ }
+
+ /**
+ * Get the difference in days using a filter closure
+ *
+ * @param Closure $callback
+ * @param \Carbon\Carbon|null $dt
+ * @param bool $abs Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInDaysFiltered(Closure $callback, Carbon $dt = null, $abs = true)
+ {
+ return $this->diffFiltered(CarbonInterval::day(), $callback, $dt, $abs);
+ }
+
+ /**
+ * Get the difference in hours using a filter closure
+ *
+ * @param Closure $callback
+ * @param \Carbon\Carbon|null $dt
+ * @param bool $abs Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInHoursFiltered(Closure $callback, Carbon $dt = null, $abs = true)
+ {
+ return $this->diffFiltered(CarbonInterval::hour(), $callback, $dt, $abs);
+ }
+
+ /**
+ * Get the difference by the given interval using a filter closure
+ *
+ * @param CarbonInterval $ci An interval to traverse by
+ * @param Closure $callback
+ * @param Carbon|null $dt
+ * @param bool $abs Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffFiltered(CarbonInterval $ci, Closure $callback, Carbon $dt = null, $abs = true)
+ {
+ $start = $this;
+ $end = $dt ?: static::now($this->getTimezone());
+ $inverse = false;
+
+ if ($end < $start) {
+ $start = $end;
+ $end = $this;
+ $inverse = true;
+ }
+
+ $period = new DatePeriod($start, $ci, $end);
+ $vals = array_filter(iterator_to_array($period), function (DateTime $date) use ($callback) {
+ return call_user_func($callback, Carbon::instance($date));
+ });
+
+ $diff = count($vals);
+
+ return $inverse && !$abs ? -$diff : $diff;
+ }
+
+ /**
+ * Get the difference in weekdays
+ *
+ * @param \Carbon\Carbon|null $dt
+ * @param bool $abs Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInWeekdays(Carbon $dt = null, $abs = true)
+ {
+ return $this->diffInDaysFiltered(function (Carbon $date) {
+ return $date->isWeekday();
+ }, $dt, $abs);
+ }
+
+ /**
+ * Get the difference in weekend days using a filter
+ *
+ * @param \Carbon\Carbon|null $dt
+ * @param bool $abs Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInWeekendDays(Carbon $dt = null, $abs = true)
+ {
+ return $this->diffInDaysFiltered(function (Carbon $date) {
+ return $date->isWeekend();
+ }, $dt, $abs);
+ }
+
+ /**
+ * Get the difference in hours
+ *
+ * @param \Carbon\Carbon|null $dt
+ * @param bool $abs Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInHours(Carbon $dt = null, $abs = true)
+ {
+ return (int) ($this->diffInSeconds($dt, $abs) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR);
+ }
+
+ /**
+ * Get the difference in minutes
+ *
+ * @param \Carbon\Carbon|null $dt
+ * @param bool $abs Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInMinutes(Carbon $dt = null, $abs = true)
+ {
+ return (int) ($this->diffInSeconds($dt, $abs) / static::SECONDS_PER_MINUTE);
+ }
+
+ /**
+ * Get the difference in seconds
+ *
+ * @param \Carbon\Carbon|null $dt
+ * @param bool $abs Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInSeconds(Carbon $dt = null, $abs = true)
+ {
+ $dt = $dt ?: static::now($this->getTimezone());
+ $value = $dt->getTimestamp() - $this->getTimestamp();
+
+ return $abs ? abs($value) : $value;
+ }
+
+ /**
+ * The number of seconds since midnight.
+ *
+ * @return int
+ */
+ public function secondsSinceMidnight()
+ {
+ return $this->diffInSeconds($this->copy()->startOfDay());
+ }
+
+ /**
+ * The number of seconds until 23:23:59.
+ *
+ * @return int
+ */
+ public function secondsUntilEndOfDay()
+ {
+ return $this->diffInSeconds($this->copy()->endOfDay());
+ }
+
+ /**
+ * Get the difference in a human readable format in the current locale.
+ *
+ * When comparing a value in the past to default now:
+ * 1 hour ago
+ * 5 months ago
+ *
+ * When comparing a value in the future to default now:
+ * 1 hour from now
+ * 5 months from now
+ *
+ * When comparing a value in the past to another value:
+ * 1 hour before
+ * 5 months before
+ *
+ * When comparing a value in the future to another value:
+ * 1 hour after
+ * 5 months after
+ *
+ * @param Carbon|null $other
+ * @param bool $absolute removes time difference modifiers ago, after, etc
+ * @param bool $short displays short format of time units
+ *
+ * @return string
+ */
+ public function diffForHumans(Carbon $other = null, $absolute = false, $short = false)
+ {
+ $isNow = $other === null;
+
+ if ($isNow) {
+ $other = static::now($this->getTimezone());
+ }
+
+ $diffInterval = $this->diff($other);
+
+ switch (true) {
+ case $diffInterval->y > 0:
+ $unit = $short ? 'y' : 'year';
+ $count = $diffInterval->y;
+ break;
+
+ case $diffInterval->m > 0:
+ $unit = $short ? 'm' : 'month';
+ $count = $diffInterval->m;
+ break;
+
+ case $diffInterval->d > 0:
+ $unit = $short ? 'd' : 'day';
+ $count = $diffInterval->d;
+
+ if ($count >= static::DAYS_PER_WEEK) {
+ $unit = $short ? 'w' : 'week';
+ $count = (int) ($count / static::DAYS_PER_WEEK);
+ }
+ break;
+
+ case $diffInterval->h > 0:
+ $unit = $short ? 'h' : 'hour';
+ $count = $diffInterval->h;
+ break;
+
+ case $diffInterval->i > 0:
+ $unit = $short ? 'min' : 'minute';
+ $count = $diffInterval->i;
+ break;
+
+ default:
+ $count = $diffInterval->s;
+ $unit = $short ? 's' : 'second';
+ break;
+ }
+
+ if ($count === 0) {
+ $count = 1;
+ }
+
+ $time = static::translator()->transChoice($unit, $count, array(':count' => $count));
+
+ if ($absolute) {
+ return $time;
+ }
+
+ $isFuture = $diffInterval->invert === 1;
+
+ $transId = $isNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before');
+
+ // Some langs have special pluralization for past and future tense.
+ $tryKeyExists = $unit.'_'.$transId;
+ if ($tryKeyExists !== static::translator()->transChoice($tryKeyExists, $count)) {
+ $time = static::translator()->transChoice($tryKeyExists, $count, array(':count' => $count));
+ }
+
+ return static::translator()->trans($transId, array(':time' => $time));
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //////////////////////////// MODIFIERS ////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Resets the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfDay()
+ {
+ return $this->setTime(0, 0, 0);
+ }
+
+ /**
+ * Resets the time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfDay()
+ {
+ return $this->setTime(23, 59, 59);
+ }
+
+ /**
+ * Resets the date to the first day of the month and the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfMonth()
+ {
+ return $this->setDateTime($this->year, $this->month, 1, 0, 0, 0);
+ }
+
+ /**
+ * Resets the date to end of the month and time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfMonth()
+ {
+ return $this->setDateTime($this->year, $this->month, $this->daysInMonth, 23, 59, 59);
+ }
+
+ /**
+ * Resets the date to the first day of the quarter and the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfQuarter()
+ {
+ $month = ($this->quarter - 1) * static::MONTHS_PER_QUARTER + 1;
+
+ return $this->setDateTime($this->year, $month, 1, 0, 0, 0);
+ }
+
+ /**
+ * Resets the date to end of the quarter and time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfQuarter()
+ {
+ return $this->startOfQuarter()->addMonths(static::MONTHS_PER_QUARTER - 1)->endOfMonth();
+ }
+
+ /**
+ * Resets the date to the first day of the year and the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfYear()
+ {
+ return $this->setDateTime($this->year, 1, 1, 0, 0, 0);
+ }
+
+ /**
+ * Resets the date to end of the year and time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfYear()
+ {
+ return $this->setDateTime($this->year, 12, 31, 23, 59, 59);
+ }
+
+ /**
+ * Resets the date to the first day of the decade and the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfDecade()
+ {
+ $year = $this->year - $this->year % static::YEARS_PER_DECADE;
+
+ return $this->setDateTime($year, 1, 1, 0, 0, 0);
+ }
+
+ /**
+ * Resets the date to end of the decade and time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfDecade()
+ {
+ $year = $this->year - $this->year % static::YEARS_PER_DECADE + static::YEARS_PER_DECADE - 1;
+
+ return $this->setDateTime($year, 12, 31, 23, 59, 59);
+ }
+
+ /**
+ * Resets the date to the first day of the century and the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfCentury()
+ {
+ $year = $this->year - ($this->year - 1) % static::YEARS_PER_CENTURY;
+
+ return $this->setDateTime($year, 1, 1, 0, 0, 0);
+ }
+
+ /**
+ * Resets the date to end of the century and time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfCentury()
+ {
+ $year = $this->year - 1 - ($this->year - 1) % static::YEARS_PER_CENTURY + static::YEARS_PER_CENTURY;
+
+ return $this->setDateTime($year, 12, 31, 23, 59, 59);
+ }
+
+ /**
+ * Resets the date to the first day of week (defined in $weekStartsAt) and the time to 00:00:00
+ *
+ * @return static
+ */
+ public function startOfWeek()
+ {
+ while ($this->dayOfWeek !== static::$weekStartsAt) {
+ $this->subDay();
+ }
+
+ return $this->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of week (defined in $weekEndsAt) and time to 23:59:59
+ *
+ * @return static
+ */
+ public function endOfWeek()
+ {
+ while ($this->dayOfWeek !== static::$weekEndsAt) {
+ $this->addDay();
+ }
+
+ return $this->endOfDay();
+ }
+
+ /**
+ * Modify to the next occurrence of a given day of the week.
+ * If no dayOfWeek is provided, modify to the next occurrence
+ * of the current day of the week. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function next($dayOfWeek = null)
+ {
+ if ($dayOfWeek === null) {
+ $dayOfWeek = $this->dayOfWeek;
+ }
+
+ return $this->startOfDay()->modify('next '.static::$days[$dayOfWeek]);
+ }
+
+ /**
+ * Go forward or backward to the next week- or weekend-day.
+ *
+ * @param bool $weekday
+ * @param bool $forward
+ *
+ * @return static
+ */
+ private function nextOrPreviousDay($weekday = true, $forward = true)
+ {
+ $step = $forward ? 1 : -1;
+
+ do {
+ $this->addDay($step);
+ } while ($weekday ? $this->isWeekend() : $this->isWeekday());
+
+ return $this;
+ }
+
+ /**
+ * Go forward to the next weekday.
+ *
+ * @return $this
+ */
+ public function nextWeekday()
+ {
+ return $this->nextOrPreviousDay();
+ }
+
+ /**
+ * Go backward to the previous weekday.
+ *
+ * @return static
+ */
+ public function previousWeekday()
+ {
+ return $this->nextOrPreviousDay(true, false);
+ }
+
+ /**
+ * Go forward to the next weekend day.
+ *
+ * @return static
+ */
+ public function nextWeekendDay()
+ {
+ return $this->nextOrPreviousDay(false);
+ }
+
+ /**
+ * Go backward to the previous weekend day.
+ *
+ * @return static
+ */
+ public function previousWeekendDay()
+ {
+ return $this->nextOrPreviousDay(false, false);
+ }
+
+ /**
+ * Modify to the previous occurrence of a given day of the week.
+ * If no dayOfWeek is provided, modify to the previous occurrence
+ * of the current day of the week. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function previous($dayOfWeek = null)
+ {
+ if ($dayOfWeek === null) {
+ $dayOfWeek = $this->dayOfWeek;
+ }
+
+ return $this->startOfDay()->modify('last '.static::$days[$dayOfWeek]);
+ }
+
+ /**
+ * Modify to the first occurrence of a given day of the week
+ * in the current month. If no dayOfWeek is provided, modify to the
+ * first day of the current month. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function firstOfMonth($dayOfWeek = null)
+ {
+ $this->startOfDay();
+
+ if ($dayOfWeek === null) {
+ return $this->day(1);
+ }
+
+ return $this->modify('first '.static::$days[$dayOfWeek].' of '.$this->format('F').' '.$this->year);
+ }
+
+ /**
+ * Modify to the last occurrence of a given day of the week
+ * in the current month. If no dayOfWeek is provided, modify to the
+ * last day of the current month. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function lastOfMonth($dayOfWeek = null)
+ {
+ $this->startOfDay();
+
+ if ($dayOfWeek === null) {
+ return $this->day($this->daysInMonth);
+ }
+
+ return $this->modify('last '.static::$days[$dayOfWeek].' of '.$this->format('F').' '.$this->year);
+ }
+
+ /**
+ * Modify to the given occurrence of a given day of the week
+ * in the current month. If the calculated occurrence is outside the scope
+ * of the current month, then return false and no modifications are made.
+ * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int $nth
+ * @param int $dayOfWeek
+ *
+ * @return mixed
+ */
+ public function nthOfMonth($nth, $dayOfWeek)
+ {
+ $dt = $this->copy()->firstOfMonth();
+ $check = $dt->format('Y-m');
+ $dt->modify('+'.$nth.' '.static::$days[$dayOfWeek]);
+
+ return $dt->format('Y-m') === $check ? $this->modify($dt) : false;
+ }
+
+ /**
+ * Modify to the first occurrence of a given day of the week
+ * in the current quarter. If no dayOfWeek is provided, modify to the
+ * first day of the current quarter. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function firstOfQuarter($dayOfWeek = null)
+ {
+ return $this->setDate($this->year, $this->quarter * static::MONTHS_PER_QUARTER - 2, 1)->firstOfMonth($dayOfWeek);
+ }
+
+ /**
+ * Modify to the last occurrence of a given day of the week
+ * in the current quarter. If no dayOfWeek is provided, modify to the
+ * last day of the current quarter. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function lastOfQuarter($dayOfWeek = null)
+ {
+ return $this->setDate($this->year, $this->quarter * static::MONTHS_PER_QUARTER, 1)->lastOfMonth($dayOfWeek);
+ }
+
+ /**
+ * Modify to the given occurrence of a given day of the week
+ * in the current quarter. If the calculated occurrence is outside the scope
+ * of the current quarter, then return false and no modifications are made.
+ * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int $nth
+ * @param int $dayOfWeek
+ *
+ * @return mixed
+ */
+ public function nthOfQuarter($nth, $dayOfWeek)
+ {
+ $dt = $this->copy()->day(1)->month($this->quarter * static::MONTHS_PER_QUARTER);
+ $lastMonth = $dt->month;
+ $year = $dt->year;
+ $dt->firstOfQuarter()->modify('+'.$nth.' '.static::$days[$dayOfWeek]);
+
+ return ($lastMonth < $dt->month || $year !== $dt->year) ? false : $this->modify($dt);
+ }
+
+ /**
+ * Modify to the first occurrence of a given day of the week
+ * in the current year. If no dayOfWeek is provided, modify to the
+ * first day of the current year. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function firstOfYear($dayOfWeek = null)
+ {
+ return $this->month(1)->firstOfMonth($dayOfWeek);
+ }
+
+ /**
+ * Modify to the last occurrence of a given day of the week
+ * in the current year. If no dayOfWeek is provided, modify to the
+ * last day of the current year. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function lastOfYear($dayOfWeek = null)
+ {
+ return $this->month(static::MONTHS_PER_YEAR)->lastOfMonth($dayOfWeek);
+ }
+
+ /**
+ * Modify to the given occurrence of a given day of the week
+ * in the current year. If the calculated occurrence is outside the scope
+ * of the current year, then return false and no modifications are made.
+ * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int $nth
+ * @param int $dayOfWeek
+ *
+ * @return mixed
+ */
+ public function nthOfYear($nth, $dayOfWeek)
+ {
+ $dt = $this->copy()->firstOfYear()->modify('+'.$nth.' '.static::$days[$dayOfWeek]);
+
+ return $this->year === $dt->year ? $this->modify($dt) : false;
+ }
+
+ /**
+ * Modify the current instance to the average of a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|null $dt
+ *
+ * @return static
+ */
+ public function average(Carbon $dt = null)
+ {
+ $dt = $dt ?: static::now($this->getTimezone());
+
+ return $this->addSeconds((int) ($this->diffInSeconds($dt, false) / 2));
+ }
+
+ /**
+ * Check if its the birthday. Compares the date/month values of the two dates.
+ *
+ * @param \Carbon\Carbon|null $dt The instance to compare with or null to use current day.
+ *
+ * @return bool
+ */
+ public function isBirthday(Carbon $dt = null)
+ {
+ return $this->isSameAs('md', $dt);
+ }
+
+ /**
+ * Consider the timezone when modifying the instance.
+ *
+ * @param string $modify
+ *
+ * @return static
+ */
+ public function modify($modify)
+ {
+ if ($this->local) {
+ return parent::modify($modify);
+ }
+
+ $timezone = $this->getTimezone();
+ $this->setTimezone('UTC');
+ $instance = parent::modify($modify);
+ $this->setTimezone($timezone);
+
+ return $instance;
+ }
+
+ /**
+ * Return a serialized string of the instance.
+ *
+ * @return string
+ */
+ public function serialize()
+ {
+ return serialize($this);
+ }
+
+ /**
+ * Create an instance form a serialized string.
+ *
+ * @param string $value
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return static
+ */
+ public static function fromSerialized($value)
+ {
+ $instance = @unserialize($value);
+
+ if (!$instance instanceof static) {
+ throw new InvalidArgumentException('Invalid serialized value.');
+ }
+
+ return $instance;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use DateInterval;
+use InvalidArgumentException;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\Translator;
+use Symfony\Component\Translation\TranslatorInterface;
+
+/**
+ * A simple API extension for DateInterval.
+ * The implementation provides helpers to handle weeks but only days are saved.
+ * Weeks are calculated based on the total days of the current instance.
+ *
+ * @property int $years Total years of the current interval.
+ * @property int $months Total months of the current interval.
+ * @property int $weeks Total weeks of the current interval calculated from the days.
+ * @property int $dayz Total days of the current interval (weeks * 7 + days).
+ * @property int $hours Total hours of the current interval.
+ * @property int $minutes Total minutes of the current interval.
+ * @property int $seconds Total seconds of the current interval.
+ * @property-read int $dayzExcludeWeeks Total days remaining in the final week of the current instance (days % 7).
+ * @property-read int $daysExcludeWeeks alias of dayzExcludeWeeks
+ *
+ * @method static CarbonInterval years($years = 1) Create instance specifying a number of years.
+ * @method static CarbonInterval year($years = 1) Alias for years()
+ * @method static CarbonInterval months($months = 1) Create instance specifying a number of months.
+ * @method static CarbonInterval month($months = 1) Alias for months()
+ * @method static CarbonInterval weeks($weeks = 1) Create instance specifying a number of weeks.
+ * @method static CarbonInterval week($weeks = 1) Alias for weeks()
+ * @method static CarbonInterval days($days = 1) Create instance specifying a number of days.
+ * @method static CarbonInterval dayz($days = 1) Alias for days()
+ * @method static CarbonInterval day($days = 1) Alias for days()
+ * @method static CarbonInterval hours($hours = 1) Create instance specifying a number of hours.
+ * @method static CarbonInterval hour($hours = 1) Alias for hours()
+ * @method static CarbonInterval minutes($minutes = 1) Create instance specifying a number of minutes.
+ * @method static CarbonInterval minute($minutes = 1) Alias for minutes()
+ * @method static CarbonInterval seconds($seconds = 1) Create instance specifying a number of seconds.
+ * @method static CarbonInterval second($seconds = 1) Alias for seconds()
+ * @method CarbonInterval years() years($years = 1) Set the years portion of the current interval.
+ * @method CarbonInterval year() year($years = 1) Alias for years().
+ * @method CarbonInterval months() months($months = 1) Set the months portion of the current interval.
+ * @method CarbonInterval month() month($months = 1) Alias for months().
+ * @method CarbonInterval weeks() weeks($weeks = 1) Set the weeks portion of the current interval. Will overwrite dayz value.
+ * @method CarbonInterval week() week($weeks = 1) Alias for weeks().
+ * @method CarbonInterval days() days($days = 1) Set the days portion of the current interval.
+ * @method CarbonInterval dayz() dayz($days = 1) Alias for days().
+ * @method CarbonInterval day() day($days = 1) Alias for days().
+ * @method CarbonInterval hours() hours($hours = 1) Set the hours portion of the current interval.
+ * @method CarbonInterval hour() hour($hours = 1) Alias for hours().
+ * @method CarbonInterval minutes() minutes($minutes = 1) Set the minutes portion of the current interval.
+ * @method CarbonInterval minute() minute($minutes = 1) Alias for minutes().
+ * @method CarbonInterval seconds() seconds($seconds = 1) Set the seconds portion of the current interval.
+ * @method CarbonInterval second() second($seconds = 1) Alias for seconds().
+ */
+class CarbonInterval extends DateInterval
+{
+ /**
+ * Interval spec period designators
+ */
+ const PERIOD_PREFIX = 'P';
+ const PERIOD_YEARS = 'Y';
+ const PERIOD_MONTHS = 'M';
+ const PERIOD_DAYS = 'D';
+ const PERIOD_TIME_PREFIX = 'T';
+ const PERIOD_HOURS = 'H';
+ const PERIOD_MINUTES = 'M';
+ const PERIOD_SECONDS = 'S';
+
+ /**
+ * A translator to ... er ... translate stuff
+ *
+ * @var \Symfony\Component\Translation\TranslatorInterface
+ */
+ protected static $translator;
+
+ /**
+ * Before PHP 5.4.20/5.5.4 instead of FALSE days will be set to -99999 when the interval instance
+ * was created by DateTime:diff().
+ */
+ const PHP_DAYS_FALSE = -99999;
+
+ /**
+ * Determine if the interval was created via DateTime:diff() or not.
+ *
+ * @param DateInterval $interval
+ *
+ * @return bool
+ */
+ private static function wasCreatedFromDiff(DateInterval $interval)
+ {
+ return $interval->days !== false && $interval->days !== static::PHP_DAYS_FALSE;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //////////////////////////// CONSTRUCTORS /////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Create a new CarbonInterval instance.
+ *
+ * @param int $years
+ * @param int $months
+ * @param int $weeks
+ * @param int $days
+ * @param int $hours
+ * @param int $minutes
+ * @param int $seconds
+ */
+ public function __construct($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null)
+ {
+ $spec = static::PERIOD_PREFIX;
+
+ $spec .= $years > 0 ? $years.static::PERIOD_YEARS : '';
+ $spec .= $months > 0 ? $months.static::PERIOD_MONTHS : '';
+
+ $specDays = 0;
+ $specDays += $weeks > 0 ? $weeks * Carbon::DAYS_PER_WEEK : 0;
+ $specDays += $days > 0 ? $days : 0;
+
+ $spec .= $specDays > 0 ? $specDays.static::PERIOD_DAYS : '';
+
+ if ($hours > 0 || $minutes > 0 || $seconds > 0) {
+ $spec .= static::PERIOD_TIME_PREFIX;
+ $spec .= $hours > 0 ? $hours.static::PERIOD_HOURS : '';
+ $spec .= $minutes > 0 ? $minutes.static::PERIOD_MINUTES : '';
+ $spec .= $seconds > 0 ? $seconds.static::PERIOD_SECONDS : '';
+ }
+
+ if ($spec === static::PERIOD_PREFIX) {
+ // Allow the zero interval.
+ $spec .= '0'.static::PERIOD_YEARS;
+ }
+
+ parent::__construct($spec);
+ }
+
+ /**
+ * Create a new CarbonInterval instance from specific values.
+ * This is an alias for the constructor that allows better fluent
+ * syntax as it allows you to do CarbonInterval::create(1)->fn() rather than
+ * (new CarbonInterval(1))->fn().
+ *
+ * @param int $years
+ * @param int $months
+ * @param int $weeks
+ * @param int $days
+ * @param int $hours
+ * @param int $minutes
+ * @param int $seconds
+ *
+ * @return static
+ */
+ public static function create($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null)
+ {
+ return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds);
+ }
+
+ /**
+ * Provide static helpers to create instances. Allows CarbonInterval::years(3).
+ *
+ * Note: This is done using the magic method to allow static and instance methods to
+ * have the same names.
+ *
+ * @param string $name
+ * @param array $args
+ *
+ * @return static
+ */
+ public static function __callStatic($name, $args)
+ {
+ $arg = count($args) === 0 ? 1 : $args[0];
+
+ switch ($name) {
+ case 'years':
+ case 'year':
+ return new static($arg);
+
+ case 'months':
+ case 'month':
+ return new static(null, $arg);
+
+ case 'weeks':
+ case 'week':
+ return new static(null, null, $arg);
+
+ case 'days':
+ case 'dayz':
+ case 'day':
+ return new static(null, null, null, $arg);
+
+ case 'hours':
+ case 'hour':
+ return new static(null, null, null, null, $arg);
+
+ case 'minutes':
+ case 'minute':
+ return new static(null, null, null, null, null, $arg);
+
+ case 'seconds':
+ case 'second':
+ return new static(null, null, null, null, null, null, $arg);
+ }
+ }
+
+ /**
+ * Create a CarbonInterval instance from a DateInterval one. Can not instance
+ * DateInterval objects created from DateTime::diff() as you can't externally
+ * set the $days field.
+ *
+ * @param DateInterval $di
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return static
+ */
+ public static function instance(DateInterval $di)
+ {
+ if (static::wasCreatedFromDiff($di)) {
+ throw new InvalidArgumentException('Can not instance a DateInterval object created from DateTime::diff().');
+ }
+
+ $instance = new static($di->y, $di->m, 0, $di->d, $di->h, $di->i, $di->s);
+ $instance->invert = $di->invert;
+ $instance->days = $di->days;
+
+ return $instance;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////// LOCALIZATION //////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Initialize the translator instance if necessary.
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ protected static function translator()
+ {
+ if (static::$translator === null) {
+ static::$translator = new Translator('en');
+ static::$translator->addLoader('array', new ArrayLoader());
+ static::setLocale('en');
+ }
+
+ return static::$translator;
+ }
+
+ /**
+ * Get the translator instance in use
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ public static function getTranslator()
+ {
+ return static::translator();
+ }
+
+ /**
+ * Set the translator instance to use
+ *
+ * @param TranslatorInterface $translator
+ */
+ public static function setTranslator(TranslatorInterface $translator)
+ {
+ static::$translator = $translator;
+ }
+
+ /**
+ * Get the current translator locale
+ *
+ * @return string
+ */
+ public static function getLocale()
+ {
+ return static::translator()->getLocale();
+ }
+
+ /**
+ * Set the current translator locale
+ *
+ * @param string $locale
+ */
+ public static function setLocale($locale)
+ {
+ static::translator()->setLocale($locale);
+
+ // Ensure the locale has been loaded.
+ static::translator()->addResource('array', require __DIR__.'/Lang/'.$locale.'.php', $locale);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ ///////////////////////// GETTERS AND SETTERS /////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Get a part of the CarbonInterval object
+ *
+ * @param string $name
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return int
+ */
+ public function __get($name)
+ {
+ switch ($name) {
+ case 'years':
+ return $this->y;
+
+ case 'months':
+ return $this->m;
+
+ case 'dayz':
+ return $this->d;
+
+ case 'hours':
+ return $this->h;
+
+ case 'minutes':
+ return $this->i;
+
+ case 'seconds':
+ return $this->s;
+
+ case 'weeks':
+ return (int) floor($this->d / Carbon::DAYS_PER_WEEK);
+
+ case 'daysExcludeWeeks':
+ case 'dayzExcludeWeeks':
+ return $this->d % Carbon::DAYS_PER_WEEK;
+
+ default:
+ throw new InvalidArgumentException(sprintf("Unknown getter '%s'", $name));
+ }
+ }
+
+ /**
+ * Set a part of the CarbonInterval object
+ *
+ * @param string $name
+ * @param int $val
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function __set($name, $val)
+ {
+ switch ($name) {
+ case 'years':
+ $this->y = $val;
+ break;
+
+ case 'months':
+ $this->m = $val;
+ break;
+
+ case 'weeks':
+ $this->d = $val * Carbon::DAYS_PER_WEEK;
+ break;
+
+ case 'dayz':
+ $this->d = $val;
+ break;
+
+ case 'hours':
+ $this->h = $val;
+ break;
+
+ case 'minutes':
+ $this->i = $val;
+ break;
+
+ case 'seconds':
+ $this->s = $val;
+ break;
+ }
+ }
+
+ /**
+ * Allow setting of weeks and days to be cumulative.
+ *
+ * @param int $weeks Number of weeks to set
+ * @param int $days Number of days to set
+ *
+ * @return static
+ */
+ public function weeksAndDays($weeks, $days)
+ {
+ $this->dayz = ($weeks * Carbon::DAYS_PER_WEEK) + $days;
+
+ return $this;
+ }
+
+ /**
+ * Allow fluent calls on the setters... CarbonInterval::years(3)->months(5)->day().
+ *
+ * Note: This is done using the magic method to allow static and instance methods to
+ * have the same names.
+ *
+ * @param string $name
+ * @param array $args
+ *
+ * @return static
+ */
+ public function __call($name, $args)
+ {
+ $arg = count($args) === 0 ? 1 : $args[0];
+
+ switch ($name) {
+ case 'years':
+ case 'year':
+ $this->years = $arg;
+ break;
+
+ case 'months':
+ case 'month':
+ $this->months = $arg;
+ break;
+
+ case 'weeks':
+ case 'week':
+ $this->dayz = $arg * Carbon::DAYS_PER_WEEK;
+ break;
+
+ case 'days':
+ case 'dayz':
+ case 'day':
+ $this->dayz = $arg;
+ break;
+
+ case 'hours':
+ case 'hour':
+ $this->hours = $arg;
+ break;
+
+ case 'minutes':
+ case 'minute':
+ $this->minutes = $arg;
+ break;
+
+ case 'seconds':
+ case 'second':
+ $this->seconds = $arg;
+ break;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the current interval in a human readable format in the current locale.
+ *
+ * @return string
+ */
+ public function forHumans()
+ {
+ $periods = array(
+ 'year' => $this->years,
+ 'month' => $this->months,
+ 'week' => $this->weeks,
+ 'day' => $this->daysExcludeWeeks,
+ 'hour' => $this->hours,
+ 'minute' => $this->minutes,
+ 'second' => $this->seconds,
+ );
+
+ $parts = array();
+ foreach ($periods as $unit => $count) {
+ if ($count > 0) {
+ array_push($parts, static::translator()->transChoice($unit, $count, array(':count' => $count)));
+ }
+ }
+
+ return implode(' ', $parts);
+ }
+
+ /**
+ * Format the instance as a string using the forHumans() function.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->forHumans();
+ }
+
+ /**
+ * Add the passed interval to the current instance
+ *
+ * @param DateInterval $interval
+ *
+ * @return static
+ */
+ public function add(DateInterval $interval)
+ {
+ $sign = $interval->invert === 1 ? -1 : 1;
+
+ if (static::wasCreatedFromDiff($interval)) {
+ $this->dayz += $interval->days * $sign;
+ } else {
+ $this->years += $interval->y * $sign;
+ $this->months += $interval->m * $sign;
+ $this->dayz += $interval->d * $sign;
+ $this->hours += $interval->h * $sign;
+ $this->minutes += $interval->i * $sign;
+ $this->seconds += $interval->s * $sign;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the interval_spec string
+ *
+ * @return string
+ */
+ public function spec()
+ {
+ $date = array_filter(array(
+ static::PERIOD_YEARS => $this->y,
+ static::PERIOD_MONTHS => $this->m,
+ static::PERIOD_DAYS => $this->d,
+ ));
+
+ $time = array_filter(array(
+ static::PERIOD_HOURS => $this->h,
+ static::PERIOD_MINUTES => $this->i,
+ static::PERIOD_SECONDS => $this->s,
+ ));
+
+ $specString = static::PERIOD_PREFIX;
+
+ foreach ($date as $key => $value) {
+ $specString .= $value.$key;
+ }
+
+ if (count($time) > 0) {
+ $specString .= static::PERIOD_TIME_PREFIX;
+ foreach ($time as $key => $value) {
+ $specString .= $value.$key;
+ }
+ }
+
+ return $specString === static::PERIOD_PREFIX ? 'PT0S' : $specString;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use Exception;
+use InvalidArgumentException;
+
+class InvalidDateException extends InvalidArgumentException
+{
+ /**
+ * The invalid field.
+ *
+ * @var string
+ */
+ private $field;
+
+ /**
+ * The invalid value.
+ *
+ * @var mixed
+ */
+ private $value;
+
+ /**
+ * Constructor.
+ *
+ * @param string $field
+ * @param mixed $value
+ * @param int $code
+ * @param \Exception|null $previous
+ */
+ public function __construct($field, $value, $code = 0, Exception $previous = null)
+ {
+ $this->field = $field;
+ $this->value = $value;
+ parent::__construct($field.' : '.$value.' is not a valid value.', $code, $previous);
+ }
+
+ /**
+ * Get the invalid field.
+ *
+ * @return string
+ */
+ public function getField()
+ {
+ return $this->field;
+ }
+
+ /**
+ * Get the invalid value.
+ *
+ * @return mixed
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 jaar|:count jare',
+ 'y' => '1 jaar|:count jare',
+ 'month' => '1 maand|:count maande',
+ 'm' => '1 maand|:count maande',
+ 'week' => '1 week|:count weke',
+ 'w' => '1 week|:count weke',
+ 'day' => '1 dag|:count dae',
+ 'd' => '1 dag|:count dae',
+ 'hour' => '1 uur|:count ure',
+ 'h' => '1 uur|:count ure',
+ 'minute' => '1 minuut|:count minute',
+ 'min' => '1 minuut|:count minute',
+ 'second' => '1 sekond|:count sekondes',
+ 's' => '1 sekond|:count sekondes',
+ 'ago' => ':time terug',
+ 'from_now' => ':time van nou af',
+ 'after' => ':time na',
+ 'before' => ':time voor',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '{0}سنة|{1}سنة|{2}سنتين|[3,10]:count سنوات|[11,Inf]:count سنة',
+ 'y' => '{0}سنة|{1}سنة|{2}سنتين|[3,10]:count سنوات|[11,Inf]:count سنة',
+ 'month' => '{0}شهر|{1} شهر|{2}شهرين|[3,10]:count أشهر|[11,Inf]:count شهر',
+ 'm' => '{0}شهر|{1} شهر|{2}شهرين|[3,10]:count أشهر|[11,Inf]:count شهر',
+ 'week' => '{0}إسبوع|{1}إسبوع|{2}إسبوعين|[3,10]:count أسابيع|[11,Inf]:count إسبوع',
+ 'w' => '{0}إسبوع|{1}إسبوع|{2}إسبوعين|[3,10]:count أسابيع|[11,Inf]:count إسبوع',
+ 'day' => '{0}يوم|{1}يوم|{2}يومين|[3,10]:count أيام|[11,Inf] يوم',
+ 'd' => '{0}يوم|{1}يوم|{2}يومين|[3,10]:count أيام|[11,Inf] يوم',
+ 'hour' => '{0}ساعة|{1}ساعة|{2}ساعتين|[3,10]:count ساعات|[11,Inf]:count ساعة',
+ 'h' => '{0}ساعة|{1}ساعة|{2}ساعتين|[3,10]:count ساعات|[11,Inf]:count ساعة',
+ 'minute' => '{0}دقيقة|{1}دقيقة|{2}دقيقتين|[3,10]:count دقائق|[11,Inf]:count دقيقة',
+ 'min' => '{0}دقيقة|{1}دقيقة|{2}دقيقتين|[3,10]:count دقائق|[11,Inf]:count دقيقة',
+ 'second' => '{0}ثانية|{1}ثانية|{2}ثانيتين|[3,10]:count ثوان|[11,Inf]:count ثانية',
+ 's' => '{0}ثانية|{1}ثانية|{2}ثانيتين|[3,10]:count ثوان|[11,Inf]:count ثانية',
+ 'ago' => 'منذ :time',
+ 'from_now' => 'من الآن :time',
+ 'after' => 'بعد :time',
+ 'before' => 'قبل :time',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count il',
+ 'y' => ':count il',
+ 'month' => ':count ay',
+ 'm' => ':count ay',
+ 'week' => ':count həftə',
+ 'w' => ':count həftə',
+ 'day' => ':count gün',
+ 'd' => ':count gün',
+ 'hour' => ':count saat',
+ 'h' => ':count saat',
+ 'minute' => ':count dəqiqə',
+ 'min' => ':count dəqiqə',
+ 'second' => ':count saniyə',
+ 's' => ':count saniyə',
+ 'ago' => ':time öncə',
+ 'from_now' => ':time sonra',
+ 'after' => ':time sonra',
+ 'before' => ':time öncə',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 година|:count години',
+ 'y' => '1 година|:count години',
+ 'month' => '1 месец|:count месеца',
+ 'm' => '1 месец|:count месеца',
+ 'week' => '1 седмица|:count седмици',
+ 'w' => '1 седмица|:count седмици',
+ 'day' => '1 ден|:count дни',
+ 'd' => '1 ден|:count дни',
+ 'hour' => '1 час|:count часа',
+ 'h' => '1 час|:count часа',
+ 'minute' => '1 минута|:count минути',
+ 'm' => '1 минута|:count минути',
+ 'second' => '1 секунда|:count секунди',
+ 's' => '1 секунда|:count секунди',
+ 'ago' => 'преди :time',
+ 'from_now' => ':time от сега',
+ 'after' => 'след :time',
+ 'before' => 'преди :time',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '১ বছর|:count বছর',
+ 'y' => '১ বছর|:count বছর',
+ 'month' => '১ মাস|:count মাস',
+ 'm' => '১ মাস|:count মাস',
+ 'week' => '১ সপ্তাহ|:count সপ্তাহ',
+ 'w' => '১ সপ্তাহ|:count সপ্তাহ',
+ 'day' => '১ দিন|:count দিন',
+ 'd' => '১ দিন|:count দিন',
+ 'hour' => '১ ঘন্টা|:count ঘন্টা',
+ 'h' => '১ ঘন্টা|:count ঘন্টা',
+ 'minute' => '১ মিনিট|:count মিনিট',
+ 'min' => '১ মিনিট|:count মিনিট',
+ 'second' => '১ সেকেন্ড|:count সেকেন্ড',
+ 's' => '১ সেকেন্ড|:count সেকেন্ড',
+ 'ago' => ':time পূর্বে',
+ 'from_now' => 'এখন থেকে :time',
+ 'after' => ':time পরে',
+ 'before' => ':time আগে',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 any|:count anys',
+ 'y' => '1 any|:count anys',
+ 'month' => '1 mes|:count mesos',
+ 'm' => '1 mes|:count mesos',
+ 'week' => '1 setmana|:count setmanes',
+ 'w' => '1 setmana|:count setmanes',
+ 'day' => '1 dia|:count dies',
+ 'd' => '1 dia|:count dies',
+ 'hour' => '1 hora|:count hores',
+ 'h' => '1 hora|:count hores',
+ 'minute' => '1 minut|:count minuts',
+ 'min' => '1 minut|:count minuts',
+ 'second' => '1 segon|:count segons',
+ 's' => '1 segon|:count segons',
+ 'ago' => 'fa :time',
+ 'from_now' => 'dins de :time',
+ 'after' => ':time després',
+ 'before' => ':time abans',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 rok|:count roky|:count let',
+ 'y' => '1 rok|:count roky|:count let',
+ 'month' => '1 měsíc|:count měsíce|:count měsíců',
+ 'm' => '1 měsíc|:count měsíce|:count měsíců',
+ 'week' => '1 týden|:count týdny|:count týdnů',
+ 'w' => '1 týden|:count týdny|:count týdnů',
+ 'day' => '1 den|:count dny|:count dní',
+ 'd' => '1 den|:count dny|:count dní',
+ 'hour' => '1 hodinu|:count hodiny|:count hodin',
+ 'h' => '1 hodinu|:count hodiny|:count hodin',
+ 'minute' => '1 minutu|:count minuty|:count minut',
+ 'min' => '1 minutu|:count minuty|:count minut',
+ 'second' => '1 sekundu|:count sekundy|:count sekund',
+ 's' => '1 sekundu|:count sekundy|:count sekund',
+ 'ago' => ':time nazpět',
+ 'from_now' => 'za :time',
+ 'after' => ':time později',
+ 'before' => ':time předtím',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 år|:count år',
+ 'y' => '1 år|:count år',
+ 'month' => '1 måned|:count måneder',
+ 'm' => '1 måned|:count måneder',
+ 'week' => '1 uge|:count uger',
+ 'w' => '1 uge|:count uger',
+ 'day' => '1 dag|:count dage',
+ 'd' => '1 dag|:count dage',
+ 'hour' => '1 time|:count timer',
+ 'h' => '1 time|:count timer',
+ 'minute' => '1 minut|:count minutter',
+ 'min' => '1 minut|:count minutter',
+ 'second' => '1 sekund|:count sekunder',
+ 's' => '1 sekund|:count sekunder',
+ 'ago' => ':time siden',
+ 'from_now' => 'om :time',
+ 'after' => ':time efter',
+ 'before' => ':time før',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 Jahr|:count Jahre',
+ 'y' => '1J|:countJ',
+ 'month' => '1 Monat|:count Monate',
+ 'm' => '1Mon|:countMon',
+ 'week' => '1 Woche|:count Wochen',
+ 'w' => '1Wo|:countWo',
+ 'day' => '1 Tag|:count Tage',
+ 'd' => '1Tg|:countTg',
+ 'hour' => '1 Stunde|:count Stunden',
+ 'h' => '1Std|:countStd',
+ 'minute' => '1 Minute|:count Minuten',
+ 'min' => '1Min|:countMin',
+ 'second' => '1 Sekunde|:count Sekunden',
+ 's' => '1Sek|:countSek',
+ 'ago' => 'vor :time',
+ 'from_now' => 'in :time',
+ 'after' => ':time später',
+ 'before' => ':time zuvor',
+
+ 'year_from_now' => '1 Jahr|:count Jahren',
+ 'month_from_now' => '1 Monat|:count Monaten',
+ 'week_from_now' => '1 Woche|:count Wochen',
+ 'day_from_now' => '1 Tag|:count Tagen',
+ 'year_ago' => '1 Jahr|:count Jahren',
+ 'month_ago' => '1 Monat|:count Monaten',
+ 'week_ago' => '1 Woche|:count Wochen',
+ 'day_ago' => '1 Tag|:count Tagen',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 χρόνος|:count χρόνια',
+ 'y' => '1 χρόνος|:count χρόνια',
+ 'month' => '1 μήνας|:count μήνες',
+ 'm' => '1 μήνας|:count μήνες',
+ 'week' => '1 εβδομάδα|:count εβδομάδες',
+ 'w' => '1 εβδομάδα|:count εβδομάδες',
+ 'day' => '1 μέρα|:count μέρες',
+ 'd' => '1 μέρα|:count μέρες',
+ 'hour' => '1 ώρα|:count ώρες',
+ 'h' => '1 ώρα|:count ώρες',
+ 'minute' => '1 λεπτό|:count λεπτά',
+ 'min' => '1 λεπτό|:count λεπτά',
+ 'second' => '1 δευτερόλεπτο|:count δευτερόλεπτα',
+ 's' => '1 δευτερόλεπτο|:count δευτερόλεπτα',
+ 'ago' => 'πρίν απο :time',
+ 'from_now' => 'σε :time απο τώρα',
+ 'after' => ':time μετά',
+ 'before' => ':time πρίν',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 year|:count years',
+ 'y' => '1yr|:countyrs',
+ 'month' => '1 month|:count months',
+ 'm' => '1mo|:countmos',
+ 'week' => '1 week|:count weeks',
+ 'w' => '1w|:countw',
+ 'day' => '1 day|:count days',
+ 'd' => '1d|:countd',
+ 'hour' => '1 hour|:count hours',
+ 'h' => '1h|:counth',
+ 'minute' => '1 minute|:count minutes',
+ 'min' => '1m|:countm',
+ 'second' => '1 second|:count seconds',
+ 's' => '1s|:counts',
+ 'ago' => ':time ago',
+ 'from_now' => ':time from now',
+ 'after' => ':time after',
+ 'before' => ':time before',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 jaro|:count jaroj',
+ 'y' => '1 jaro|:count jaroj',
+ 'month' => '1 monato|:count monatoj',
+ 'm' => '1 monato|:count monatoj',
+ 'week' => '1 semajno|:count semajnoj',
+ 'w' => '1 semajno|:count semajnoj',
+ 'day' => '1 tago|:count tagoj',
+ 'd' => '1 tago|:count tagoj',
+ 'hour' => '1 horo|:count horoj',
+ 'h' => '1 horo|:count horoj',
+ 'minute' => '1 minuto|:count minutoj',
+ 'min' => '1 minuto|:count minutoj',
+ 'second' => '1 sekundo|:count sekundoj',
+ 's' => '1 sekundo|:count sekundoj',
+ 'ago' => 'antaŭ :time',
+ 'from_now' => 'je :time',
+ 'after' => ':time poste',
+ 'before' => ':time antaŭe',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 año|:count años',
+ 'y' => '1 año|:count años',
+ 'month' => '1 mes|:count meses',
+ 'm' => '1 mes|:count meses',
+ 'week' => '1 semana|:count semanas',
+ 'w' => '1 semana|:count semanas',
+ 'day' => '1 día|:count días',
+ 'd' => '1 día|:count días',
+ 'hour' => '1 hora|:count horas',
+ 'h' => '1 hora|:count horas',
+ 'minute' => '1 minuto|:count minutos',
+ 'min' => '1 minuto|:count minutos',
+ 'second' => '1 segundo|:count segundos',
+ 's' => '1 segundo|:count segundos',
+ 'ago' => 'hace :time',
+ 'from_now' => 'dentro de :time',
+ 'after' => ':time después',
+ 'before' => ':time antes',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 aasta|:count aastat',
+ 'y' => '1 aasta|:count aastat',
+ 'month' => '1 kuu|:count kuud',
+ 'm' => '1 kuu|:count kuud',
+ 'week' => '1 nädal|:count nädalat',
+ 'w' => '1 nädal|:count nädalat',
+ 'day' => '1 päev|:count päeva',
+ 'd' => '1 päev|:count päeva',
+ 'hour' => '1 tund|:count tundi',
+ 'h' => '1 tund|:count tundi',
+ 'minute' => '1 minut|:count minutit',
+ 'min' => '1 minut|:count minutit',
+ 'second' => '1 sekund|:count sekundit',
+ 's' => '1 sekund|:count sekundit',
+ 'ago' => ':time tagasi',
+ 'from_now' => ':time pärast',
+ 'after' => ':time pärast',
+ 'before' => ':time enne',
+ 'year_from_now' => ':count aasta',
+ 'month_from_now' => ':count kuu',
+ 'week_from_now' => ':count nädala',
+ 'day_from_now' => ':count päeva',
+ 'hour_from_now' => ':count tunni',
+ 'minute_from_now' => ':count minuti',
+ 'second_from_now' => ':count sekundi',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => 'Urte 1|:count urte',
+ 'y' => 'Urte 1|:count urte',
+ 'month' => 'Hile 1|:count hile',
+ 'm' => 'Hile 1|:count hile',
+ 'week' => 'Aste 1|:count aste',
+ 'w' => 'Aste 1|:count aste',
+ 'day' => 'Egun 1|:count egun',
+ 'd' => 'Egun 1|:count egun',
+ 'hour' => 'Ordu 1|:count ordu',
+ 'h' => 'Ordu 1|:count ordu',
+ 'minute' => 'Minutu 1|:count minutu',
+ 'min' => 'Minutu 1|:count minutu',
+ 'second' => 'Segundu 1|:count segundu',
+ 's' => 'Segundu 1|:count segundu',
+ 'ago' => 'Orain dela :time',
+ 'from_now' => ':time barru',
+ 'after' => ':time geroago',
+ 'before' => ':time lehenago',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count سال',
+ 'y' => ':count سال',
+ 'month' => ':count ماه',
+ 'm' => ':count ماه',
+ 'week' => ':count هفته',
+ 'w' => ':count هفته',
+ 'day' => ':count روز',
+ 'd' => ':count روز',
+ 'hour' => ':count ساعت',
+ 'h' => ':count ساعت',
+ 'minute' => ':count دقیقه',
+ 'min' => ':count دقیقه',
+ 'second' => ':count ثانیه',
+ 's' => ':count ثانیه',
+ 'ago' => ':time پیش',
+ 'from_now' => ':time بعد',
+ 'after' => ':time پس از',
+ 'before' => ':time پیش از',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 vuosi|:count vuotta',
+ 'y' => '1 vuosi|:count vuotta',
+ 'month' => '1 kuukausi|:count kuukautta',
+ 'm' => '1 kuukausi|:count kuukautta',
+ 'week' => '1 viikko|:count viikkoa',
+ 'w' => '1 viikko|:count viikkoa',
+ 'day' => '1 päivä|:count päivää',
+ 'd' => '1 päivä|:count päivää',
+ 'hour' => '1 tunti|:count tuntia',
+ 'h' => '1 tunti|:count tuntia',
+ 'minute' => '1 minuutti|:count minuuttia',
+ 'min' => '1 minuutti|:count minuuttia',
+ 'second' => '1 sekunti|:count sekuntia',
+ 's' => '1 sekunti|:count sekuntia',
+ 'ago' => ':time sitten',
+ 'from_now' => ':time tästä hetkestä',
+ 'after' => ':time sen jälkeen',
+ 'before' => ':time ennen',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 ár|:count ár',
+ 'y' => '1 ár|:count ár',
+ 'month' => '1 mánaður|:count mánaðir',
+ 'm' => '1 mánaður|:count mánaðir',
+ 'week' => '1 vika|:count vikur',
+ 'w' => '1 vika|:count vikur',
+ 'day' => '1 dag|:count dagar',
+ 'd' => '1 dag|:count dagar',
+ 'hour' => '1 tími|:count tímar',
+ 'h' => '1 tími|:count tímar',
+ 'minute' => '1 minutt|:count minuttir',
+ 'min' => '1 minutt|:count minuttir',
+ 'second' => '1 sekund|:count sekundir',
+ 's' => '1 sekund|:count sekundir',
+ 'ago' => ':time síðan',
+ 'from_now' => 'um :time',
+ 'after' => ':time aftaná',
+ 'before' => ':time áðrenn',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 an|:count ans',
+ 'y' => '1 an|:count ans',
+ 'month' => ':count mois',
+ 'm' => ':count mois',
+ 'week' => '1 semaine|:count semaines',
+ 'w' => '1 semaine|:count semaines',
+ 'day' => '1 jour|:count jours',
+ 'd' => '1 jour|:count jours',
+ 'hour' => '1 heure|:count heures',
+ 'h' => '1 heure|:count heures',
+ 'minute' => '1 minute|:count minutes',
+ 'min' => '1 minute|:count minutes',
+ 'second' => '1 seconde|:count secondes',
+ 's' => '1 seconde|:count secondes',
+ 'ago' => 'il y a :time',
+ 'from_now' => 'dans :time',
+ 'after' => ':time après',
+ 'before' => ':time avant',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 ano|:count anos',
+ 'month' => '1 mes|:count meses',
+ 'week' => '1 semana|:count semanas',
+ 'day' => '1 día|:count días',
+ 'hour' => '1 hora|:count horas',
+ 'minute' => '1 minuto|:count minutos',
+ 'second' => '1 segundo|:count segundos',
+ 'ago' => 'fai :time',
+ 'from_now' => 'dentro de :time',
+ 'after' => ':time despois',
+ 'before' => ':time antes',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => 'שנה|{2}שנתיים|:count שנים',
+ 'y' => 'שנה|{2}שנתיים|:count שנים',
+ 'month' => 'חודש|{2}חודשיים|:count חודשים',
+ 'm' => 'חודש|{2}חודשיים|:count חודשים',
+ 'week' => 'שבוע|{2}שבועיים|:count שבועות',
+ 'w' => 'שבוע|{2}שבועיים|:count שבועות',
+ 'day' => 'יום|{2}יומיים|:count ימים',
+ 'd' => 'יום|{2}יומיים|:count ימים',
+ 'hour' => 'שעה|{2}שעתיים|:count שעות',
+ 'h' => 'שעה|{2}שעתיים|:count שעות',
+ 'minute' => 'דקה|{2}דקותיים|:count דקות',
+ 'min' => 'דקה|{2}דקותיים|:count דקות',
+ 'second' => 'שניה|:count שניות',
+ 's' => 'שניה|:count שניות',
+ 'ago' => 'לפני :time',
+ 'from_now' => 'בעוד :time',
+ 'after' => 'אחרי :time',
+ 'before' => 'לפני :time',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count godinu|:count godine|:count godina',
+ 'y' => ':count godinu|:count godine|:count godina',
+ 'month' => ':count mjesec|:count mjeseca|:count mjeseci',
+ 'm' => ':count mjesec|:count mjeseca|:count mjeseci',
+ 'week' => ':count tjedan|:count tjedna|:count tjedana',
+ 'w' => ':count tjedan|:count tjedna|:count tjedana',
+ 'day' => ':count dan|:count dana|:count dana',
+ 'd' => ':count dan|:count dana|:count dana',
+ 'hour' => ':count sat|:count sata|:count sati',
+ 'h' => ':count sat|:count sata|:count sati',
+ 'minute' => ':count minutu|:count minute |:count minuta',
+ 'min' => ':count minutu|:count minute |:count minuta',
+ 'second' => ':count sekundu|:count sekunde|:count sekundi',
+ 's' => ':count sekundu|:count sekunde|:count sekundi',
+ 'ago' => 'prije :time',
+ 'from_now' => 'za :time',
+ 'after' => 'za :time',
+ 'before' => 'prije :time',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count év',
+ 'y' => ':count év',
+ 'month' => ':count hónap',
+ 'm' => ':count hónap',
+ 'week' => ':count hét',
+ 'w' => ':count hét',
+ 'day' => ':count nap',
+ 'd' => ':count nap',
+ 'hour' => ':count óra',
+ 'h' => ':count óra',
+ 'minute' => ':count perc',
+ 'min' => ':count perc',
+ 'second' => ':count másodperc',
+ 's' => ':count másodperc',
+ 'ago' => ':time',
+ 'from_now' => ':time múlva',
+ 'after' => ':time később',
+ 'before' => ':time korábban',
+ 'year_ago' => ':count éve',
+ 'month_ago' => ':count hónapja',
+ 'week_ago' => ':count hete',
+ 'day_ago' => ':count napja',
+ 'hour_ago' => ':count órája',
+ 'minute_ago' => ':count perce',
+ 'second_ago' => ':count másodperce',
+ 'year_after' => ':count évvel',
+ 'month_after' => ':count hónappal',
+ 'week_after' => ':count héttel',
+ 'day_after' => ':count nappal',
+ 'hour_after' => ':count órával',
+ 'minute_after' => ':count perccel',
+ 'second_after' => ':count másodperccel',
+ 'year_before' => ':count évvel',
+ 'month_before' => ':count hónappal',
+ 'week_before' => ':count héttel',
+ 'day_before' => ':count nappal',
+ 'hour_before' => ':count órával',
+ 'minute_before' => ':count perccel',
+ 'second_before' => ':count másodperccel',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count տարի',
+ 'y' => ':count տարի',
+ 'month' => ':count ամիս',
+ 'm' => ':count ամիս',
+ 'week' => ':count շաբաթ',
+ 'w' => ':count շաբաթ',
+ 'day' => ':count օր',
+ 'd' => ':count օր',
+ 'hour' => ':count ժամ',
+ 'h' => ':count ժամ',
+ 'minute' => ':count րոպե',
+ 'min' => ':count րոպե',
+ 'second' => ':count վայրկյան',
+ 's' => ':count վայրկյան',
+ 'ago' => ':time առաջ',
+ 'from_now' => ':time հետո',
+ 'after' => ':time հետո',
+ 'before' => ':time առաջ',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count tahun',
+ 'y' => ':count tahun',
+ 'month' => ':count bulan',
+ 'm' => ':count bulan',
+ 'week' => ':count minggu',
+ 'w' => ':count minggu',
+ 'day' => ':count hari',
+ 'd' => ':count hari',
+ 'hour' => ':count jam',
+ 'h' => ':count jam',
+ 'minute' => ':count menit',
+ 'min' => ':count menit',
+ 'second' => ':count detik',
+ 's' => ':count detik',
+ 'ago' => ':time yang lalu',
+ 'from_now' => ':time dari sekarang',
+ 'after' => ':time setelah',
+ 'before' => ':time sebelum',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 anno|:count anni',
+ 'y' => '1 anno|:count anni',
+ 'month' => '1 mese|:count mesi',
+ 'm' => '1 mese|:count mesi',
+ 'week' => '1 settimana|:count settimane',
+ 'w' => '1 settimana|:count settimane',
+ 'day' => '1 giorno|:count giorni',
+ 'd' => '1 giorno|:count giorni',
+ 'hour' => '1 ora|:count ore',
+ 'h' => '1 ora|:count ore',
+ 'minute' => '1 minuto|:count minuti',
+ 'min' => '1 minuto|:count minuti',
+ 'second' => '1 secondo|:count secondi',
+ 's' => '1 secondo|:count secondi',
+ 'ago' => ':time fa',
+ 'from_now' => ':time da adesso',
+ 'after' => ':time dopo',
+ 'before' => ':time prima',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count 年',
+ 'y' => ':count 年',
+ 'month' => ':count ヶ月',
+ 'm' => ':count ヶ月',
+ 'week' => ':count 週間',
+ 'w' => ':count 週間',
+ 'day' => ':count 日',
+ 'd' => ':count 日',
+ 'hour' => ':count 時間',
+ 'h' => ':count 時間',
+ 'minute' => ':count 分',
+ 'min' => ':count 分',
+ 'second' => ':count 秒',
+ 's' => ':count 秒',
+ 'ago' => ':time 前',
+ 'from_now' => '今から :time',
+ 'after' => ':time 後',
+ 'before' => ':time 前',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count წლის',
+ 'y' => ':count წლის',
+ 'month' => ':count თვის',
+ 'm' => ':count თვის',
+ 'week' => ':count კვირის',
+ 'w' => ':count კვირის',
+ 'day' => ':count დღის',
+ 'd' => ':count დღის',
+ 'hour' => ':count საათის',
+ 'h' => ':count საათის',
+ 'minute' => ':count წუთის',
+ 'min' => ':count წუთის',
+ 'second' => ':count წამის',
+ 's' => ':count წამის',
+ 'ago' => ':time უკან',
+ 'from_now' => ':time შემდეგ',
+ 'after' => ':time შემდეგ',
+ 'before' => ':time უკან',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count ឆ្នាំ',
+ 'y' => ':count ឆ្នាំ',
+ 'month' => ':count ខែ',
+ 'm' => ':count ខែ',
+ 'week' => ':count សប្ដាហ៍',
+ 'w' => ':count សប្ដាហ៍',
+ 'day' => ':count ថ្ងៃ',
+ 'd' => ':count ថ្ងៃ',
+ 'hour' => ':count ម៉ោង',
+ 'h' => ':count ម៉ោង',
+ 'minute' => ':count នាទី',
+ 'min' => ':count នាទី',
+ 'second' => ':count វិនាទី',
+ 's' => ':count វិនាទី',
+ 'ago' => ':timeមុន',
+ 'from_now' => ':timeពីឥឡូវ',
+ 'after' => 'នៅក្រោយ :time',
+ 'before' => 'នៅមុន :time',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count 년',
+ 'y' => ':count 년',
+ 'month' => ':count 개월',
+ 'm' => ':count 개월',
+ 'week' => ':count 주일',
+ 'w' => ':count 주일',
+ 'day' => ':count 일',
+ 'd' => ':count 일',
+ 'hour' => ':count 시간',
+ 'h' => ':count 시간',
+ 'minute' => ':count 분',
+ 'min' => ':count 분',
+ 'second' => ':count 초',
+ 's' => ':count 초',
+ 'ago' => ':time 전',
+ 'from_now' => ':time 후',
+ 'after' => ':time 뒤',
+ 'before' => ':time 앞',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count metus|:count metus|:count metų',
+ 'y' => ':count metus|:count metus|:count metų',
+ 'month' => ':count mėnesį|:count mėnesius|:count mėnesių',
+ 'm' => ':count mėnesį|:count mėnesius|:count mėnesių',
+ 'week' => ':count savaitę|:count savaites|:count savaičių',
+ 'w' => ':count savaitę|:count savaites|:count savaičių',
+ 'day' => ':count dieną|:count dienas|:count dienų',
+ 'd' => ':count dieną|:count dienas|:count dienų',
+ 'hour' => ':count valandą|:count valandas|:count valandų',
+ 'h' => ':count valandą|:count valandas|:count valandų',
+ 'minute' => ':count minutę|:count minutes|:count minučių',
+ 'min' => ':count minutę|:count minutes|:count minučių',
+ 'second' => ':count sekundę|:count sekundes|:count sekundžių',
+ 's' => ':count sekundę|:count sekundes|:count sekundžių',
+ 'second_from_now' => ':count sekundės|:count sekundžių|:count sekundžių',
+ 'minute_from_now' => ':count minutės|:count minučių|:count minučių',
+ 'hour_from_now' => ':count valandos|:count valandų|:count valandų',
+ 'day_from_now' => ':count dienos|:count dienų|:count dienų',
+ 'week_from_now' => ':count savaitės|:count savaičių|:count savaičių',
+ 'month_from_now' => ':count mėnesio|:count mėnesių|:count mėnesių',
+ 'year_from_now' => ':count metų',
+ 'ago' => 'prieš :time',
+ 'from_now' => 'už :time',
+ 'after' => 'po :time',
+ 'before' => ':time nuo dabar',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '0 gadiem|:count gada|:count gadiem',
+ 'y' => '0 gadiem|:count gada|:count gadiem',
+ 'month' => '0 mēnešiem|:count mēneša|:count mēnešiem',
+ 'm' => '0 mēnešiem|:count mēneša|:count mēnešiem',
+ 'week' => '0 nedēļām|:count nedēļas|:count nedēļām',
+ 'w' => '0 nedēļām|:count nedēļas|:count nedēļām',
+ 'day' => '0 dienām|:count dienas|:count dienām',
+ 'd' => '0 dienām|:count dienas|:count dienām',
+ 'hour' => '0 stundām|:count stundas|:count stundām',
+ 'h' => '0 stundām|:count stundas|:count stundām',
+ 'minute' => '0 minūtēm|:count minūtes|:count minūtēm',
+ 'min' => '0 minūtēm|:count minūtes|:count minūtēm',
+ 'second' => '0 sekundēm|:count sekundes|:count sekundēm',
+ 's' => '0 sekundēm|:count sekundes|:count sekundēm',
+ 'ago' => 'pirms :time',
+ 'from_now' => 'pēc :time',
+ 'after' => ':time vēlāk',
+ 'before' => ':time pirms',
+
+ 'year_after' => '0 gadus|:count gadu|:count gadus',
+ 'month_after' => '0 mēnešus|:count mēnesi|:count mēnešus',
+ 'week_after' => '0 nedēļas|:count nedēļu|:count nedēļas',
+ 'day_after' => '0 dienas|:count dienu|:count dienas',
+ 'hour_after' => '0 stundas|:count stundu|:count stundas',
+ 'minute_after' => '0 minūtes|:count minūti|:count minūtes',
+ 'second_after' => '0 sekundes|:count sekundi|:count sekundes',
+
+ 'year_before' => '0 gadus|:count gadu|:count gadus',
+ 'month_before' => '0 mēnešus|:count mēnesi|:count mēnešus',
+ 'week_before' => '0 nedēļas|:count nedēļu|:count nedēļas',
+ 'day_before' => '0 dienas|:count dienu|:count dienas',
+ 'hour_before' => '0 stundas|:count stundu|:count stundas',
+ 'minute_before' => '0 minūtes|:count minūti|:count minūtes',
+ 'second_before' => '0 sekundes|:count sekundi|:count sekundes',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 година|:count години',
+ 'month' => '1 месец|:count месеци',
+ 'week' => '1 седмица|:count седмици',
+ 'day' => '1 ден|:count дена',
+ 'hour' => '1 час|:count часа',
+ 'minute' => '1 минута|:count минути',
+ 'second' => '1 секунда|:count секунди',
+ 'ago' => 'пред :time',
+ 'from_now' => ':time од сега',
+ 'after' => 'по :time',
+ 'before' => 'пред :time',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count tahun',
+ 'y' => ':count tahun',
+ 'month' => ':count bulan',
+ 'm' => ':count bulan',
+ 'week' => ':count minggu',
+ 'w' => ':count minggu',
+ 'day' => ':count hari',
+ 'd' => ':count hari',
+ 'hour' => ':count jam',
+ 'h' => ':count jam',
+ 'minute' => ':count minit',
+ 'min' => ':count minit',
+ 'second' => ':count saat',
+ 's' => ':count saat',
+ 'ago' => ':time yang lalu',
+ 'from_now' => ':time dari sekarang',
+ 'after' => ':time selepas',
+ 'before' => ':time sebelum',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count jaar',
+ 'y' => ':count jaar',
+ 'month' => '1 maand|:count maanden',
+ 'm' => '1 maand|:count maanden',
+ 'week' => '1 week|:count weken',
+ 'w' => '1 week|:count weken',
+ 'day' => '1 dag|:count dagen',
+ 'd' => '1 dag|:count dagen',
+ 'hour' => ':count uur',
+ 'h' => ':count uur',
+ 'minute' => '1 minuut|:count minuten',
+ 'min' => '1 minuut|:count minuten',
+ 'second' => '1 seconde|:count seconden',
+ 's' => '1 seconde|:count seconden',
+ 'ago' => ':time geleden',
+ 'from_now' => 'over :time',
+ 'after' => ':time later',
+ 'before' => ':time eerder',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 år|:count år',
+ 'y' => '1 år|:count år',
+ 'month' => '1 måned|:count måneder',
+ 'm' => '1 måned|:count måneder',
+ 'week' => '1 uke|:count uker',
+ 'w' => '1 uke|:count uker',
+ 'day' => '1 dag|:count dager',
+ 'd' => '1 dag|:count dager',
+ 'hour' => '1 time|:count timer',
+ 'h' => '1 time|:count timer',
+ 'minute' => '1 minutt|:count minutter',
+ 'min' => '1 minutt|:count minutter',
+ 'second' => '1 sekund|:count sekunder',
+ 's' => '1 sekund|:count sekunder',
+ 'ago' => ':time siden',
+ 'from_now' => 'om :time',
+ 'after' => ':time etter',
+ 'before' => ':time før',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 rok|:count lata|:count lat',
+ 'y' => '1 rok|:count lata|:count lat',
+ 'month' => '1 miesiąc|:count miesiące|:count miesięcy',
+ 'm' => '1 miesiąc|:count miesiące|:count miesięcy',
+ 'week' => '1 tydzień|:count tygodnie|:count tygodni',
+ 'w' => '1 tydzień|:count tygodnie|:count tygodni',
+ 'day' => '1 dzień|:count dni|:count dni',
+ 'd' => '1 dzień|:count dni|:count dni',
+ 'hour' => '1 godzina|:count godziny|:count godzin',
+ 'h' => '1 godzina|:count godziny|:count godzin',
+ 'minute' => '1 minuta|:count minuty|:count minut',
+ 'min' => '1 minuta|:count minuty|:count minut',
+ 'second' => '1 sekunda|:count sekundy|:count sekund',
+ 's' => '1 sekunda|:count sekundy|:count sekund',
+ 'ago' => ':time temu',
+ 'from_now' => ':time od teraz',
+ 'after' => ':time przed',
+ 'before' => ':time po',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 ano|:count anos',
+ 'y' => '1 ano|:count anos',
+ 'month' => '1 mês|:count meses',
+ 'm' => '1 mês|:count meses',
+ 'week' => '1 semana|:count semanas',
+ 'w' => '1 semana|:count semanas',
+ 'day' => '1 dia|:count dias',
+ 'd' => '1 dia|:count dias',
+ 'hour' => '1 hora|:count horas',
+ 'h' => '1 hora|:count horas',
+ 'minute' => '1 minuto|:count minutos',
+ 'min' => '1 minuto|:count minutos',
+ 'second' => '1 segundo|:count segundos',
+ 's' => '1 segundo|:count segundos',
+ 'ago' => ':time atrás',
+ 'from_now' => 'em :time',
+ 'after' => ':time depois',
+ 'before' => ':time antes',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 ano|:count anos',
+ 'y' => '1 ano|:count anos',
+ 'month' => '1 mês|:count meses',
+ 'm' => '1 mês|:count meses',
+ 'week' => '1 semana|:count semanas',
+ 'w' => '1 semana|:count semanas',
+ 'day' => '1 dia|:count dias',
+ 'd' => '1 dia|:count dias',
+ 'hour' => '1 hora|:count horas',
+ 'h' => '1 hora|:count horas',
+ 'minute' => '1 minuto|:count minutos',
+ 'min' => '1 minuto|:count minutos',
+ 'second' => '1 segundo|:count segundos',
+ 's' => '1 segundo|:count segundos',
+ 'ago' => 'há :time',
+ 'from_now' => 'em :time',
+ 'after' => 'após :time',
+ 'before' => ':time atrás',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => 'un an|:count ani|:count ani',
+ 'y' => 'un an|:count ani|:count ani',
+ 'month' => 'o lună|:count luni|:count luni',
+ 'm' => 'o lună|:count luni|:count luni',
+ 'week' => 'o săptămână|:count săptămâni|:count săptămâni',
+ 'w' => 'o săptămână|:count săptămâni|:count săptămâni',
+ 'day' => 'o zi|:count zile|:count zile',
+ 'd' => 'o zi|:count zile|:count zile',
+ 'hour' => 'o oră|:count ore|:count ore',
+ 'h' => 'o oră|:count ore|:count ore',
+ 'minute' => 'un minut|:count minute|:count minute',
+ 'min' => 'un minut|:count minute|:count minute',
+ 'second' => 'o secundă|:count secunde|:count secunde',
+ 's' => 'o secundă|:count secunde|:count secunde',
+ 'ago' => 'acum :time',
+ 'from_now' => ':time de acum',
+ 'after' => 'peste :time',
+ 'before' => 'acum :time',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count год|:count года|:count лет',
+ 'y' => ':count год|:count года|:count лет',
+ 'month' => ':count месяц|:count месяца|:count месяцев',
+ 'm' => ':count месяц|:count месяца|:count месяцев',
+ 'week' => ':count неделю|:count недели|:count недель',
+ 'w' => ':count неделю|:count недели|:count недель',
+ 'day' => ':count день|:count дня|:count дней',
+ 'd' => ':count день|:count дня|:count дней',
+ 'hour' => ':count час|:count часа|:count часов',
+ 'h' => ':count час|:count часа|:count часов',
+ 'minute' => ':count минуту|:count минуты|:count минут',
+ 'min' => ':count минуту|:count минуты|:count минут',
+ 'second' => ':count секунду|:count секунды|:count секунд',
+ 's' => ':count секунду|:count секунды|:count секунд',
+ 'ago' => ':time назад',
+ 'from_now' => 'через :time',
+ 'after' => ':time после',
+ 'before' => ':time до',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => 'rok|:count roky|:count rokov',
+ 'y' => 'rok|:count roky|:count rokov',
+ 'month' => 'mesiac|:count mesiace|:count mesiacov',
+ 'm' => 'mesiac|:count mesiace|:count mesiacov',
+ 'week' => 'týždeň|:count týždne|:count týždňov',
+ 'w' => 'týždeň|:count týždne|:count týždňov',
+ 'day' => 'deň|:count dni|:count dní',
+ 'd' => 'deň|:count dni|:count dní',
+ 'hour' => 'hodinu|:count hodiny|:count hodín',
+ 'h' => 'hodinu|:count hodiny|:count hodín',
+ 'minute' => 'minútu|:count minúty|:count minút',
+ 'min' => 'minútu|:count minúty|:count minút',
+ 'second' => 'sekundu|:count sekundy|:count sekúnd',
+ 's' => 'sekundu|:count sekundy|:count sekúnd',
+ 'ago' => 'pred :time',
+ 'from_now' => 'za :time',
+ 'after' => ':time neskôr',
+ 'before' => ':time predtým',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count leto|:count leti|:count leta|:count let',
+ 'y' => ':count leto|:count leti|:count leta|:count let',
+ 'month' => ':count mesec|:count meseca|:count mesece|:count mesecev',
+ 'm' => ':count mesec|:count meseca|:count mesece|:count mesecev',
+ 'week' => ':count teden|:count tedna|:count tedne|:count tednov',
+ 'w' => ':count teden|:count tedna|:count tedne|:count tednov',
+ 'day' => ':count dan|:count dni|:count dni|:count dni',
+ 'd' => ':count dan|:count dni|:count dni|:count dni',
+ 'hour' => ':count uro|:count uri|:count ure|:count ur',
+ 'h' => ':count uro|:count uri|:count ure|:count ur',
+ 'minute' => ':count minuto|:count minuti|:count minute|:count minut',
+ 'min' => ':count minuto|:count minuti|:count minute|:count minut',
+ 'second' => ':count sekundo|:count sekundi|:count sekunde|:count sekund',
+ 's' => ':count sekundo|:count sekundi|:count sekunde|:count sekund',
+ 'year_ago' => ':count letom|:count leti|:count leti|:count leti',
+ 'month_ago' => ':count mesecem|:count meseci|:count meseci|:count meseci',
+ 'week_ago' => ':count tednom|:count tednoma|:count tedni|:count tedni',
+ 'day_ago' => ':count dnem|:count dnevoma|:count dnevi|:count dnevi',
+ 'hour_ago' => ':count uro|:count urama|:count urami|:count urami',
+ 'minute_ago' => ':count minuto|:count minutama|:count minutami|:count minutami',
+ 'second_ago' => ':count sekundo|:count sekundama|:count sekundami|:count sekundami',
+ 'ago' => 'pred :time',
+ 'from_now' => 'čez :time',
+ 'after' => 'čez :time',
+ 'before' => 'pred :time',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 vit|:count vjet',
+ 'y' => '1 vit|:count vjet',
+ 'month' => '1 muaj|:count muaj',
+ 'm' => '1 muaj|:count muaj',
+ 'week' => '1 javë|:count javë',
+ 'w' => '1 javë|:count javë',
+ 'day' => '1 ditë|:count ditë',
+ 'd' => '1 ditë|:count ditë',
+ 'hour' => '1 orë|:count orë',
+ 'h' => '1 orë|:count orë',
+ 'minute' => '1 minutë|:count minuta',
+ 'min' => '1 minutë|:count minuta',
+ 'second' => '1 sekondë|:count sekonda',
+ 's' => '1 sekondë|:count sekonda',
+ 'ago' => ':time më parë',
+ 'from_now' => ':time nga tani',
+ 'after' => ':time pas',
+ 'before' => ':time para',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count godina|:count godine|:count godina',
+ 'y' => ':count godina|:count godine|:count godina',
+ 'month' => ':count mesec|:count meseca|:count meseci',
+ 'm' => ':count mesec|:count meseca|:count meseci',
+ 'week' => ':count nedelja|:count nedelje|:count nedelja',
+ 'w' => ':count nedelja|:count nedelje|:count nedelja',
+ 'day' => ':count dan|:count dana|:count dana',
+ 'd' => ':count dan|:count dana|:count dana',
+ 'hour' => ':count sat|:count sata|:count sati',
+ 'h' => ':count sat|:count sata|:count sati',
+ 'minute' => ':count minut|:count minuta |:count minuta',
+ 'min' => ':count minut|:count minuta |:count minuta',
+ 'second' => ':count sekund|:count sekunde|:count sekunde',
+ 's' => ':count sekund|:count sekunde|:count sekunde',
+ 'ago' => 'pre :time',
+ 'from_now' => ':time od sada',
+ 'after' => 'nakon :time',
+ 'before' => 'pre :time',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count године|[0,Inf[ :count година',
+ 'y' => ':count г.',
+ 'month' => '{1} :count мјесец|{2,3,4}:count мјесеца|[5,Inf[ :count мјесеци',
+ 'm' => ':count мј.',
+ 'week' => '{1} :count недјеља|{2,3,4}:count недјеље|[5,Inf[ :count недјеља',
+ 'w' => ':count нед.',
+ 'day' => '{1,21,31} :count дан|[2,Inf[ :count дана',
+ 'd' => ':count д.',
+ 'hour' => '{1,21} :count сат|{2,3,4,22,23,24}:count сата|[5,Inf[ :count сати',
+ 'h' => ':count ч.',
+ 'minute' => '{1,21,31,41,51} :count минут|[2,Inf[ :count минута',
+ 'min' => ':count мин.',
+ 'second' => '{1,21,31,41,51} :count секунд|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count секунде|[5,Inf[:count секунди',
+ 's' => ':count сек.',
+ 'ago' => 'прије :time',
+ 'from_now' => 'за :time',
+ 'after' => ':time након',
+ 'before' => ':time прије',
+
+ 'year_from_now' => '{1,21,31,41,51} :count годину|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count године|[5,Inf[ :count година',
+ 'year_ago' => '{1,21,31,41,51} :count годину|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count године|[5,Inf[ :count година',
+
+ 'week_from_now' => '{1} :count недјељу|{2,3,4} :count недјеље|[5,Inf[ :count недјеља',
+ 'week_ago' => '{1} :count недјељу|{2,3,4} :count недјеље|[5,Inf[ :count недјеља',
+
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count godine|[0,Inf[ :count godina',
+ 'y' => ':count g.',
+ 'month' => '{1} :count mjesec|{2,3,4}:count mjeseca|[5,Inf[ :count mjeseci',
+ 'm' => ':count mj.',
+ 'week' => '{1} :count nedjelja|{2,3,4}:count nedjelje|[5,Inf[ :count nedjelja',
+ 'w' => ':count ned.',
+ 'day' => '{1,21,31} :count dan|[2,Inf[ :count dana',
+ 'd' => ':count d.',
+ 'hour' => '{1,21} :count sat|{2,3,4,22,23,24}:count sata|[5,Inf[ :count sati',
+ 'h' => ':count č.',
+ 'minute' => '{1,21,31,41,51} :count minut|[2,Inf[ :count minuta',
+ 'min' => ':count min.',
+ 'second' => '{1,21,31,41,51} :count sekund|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count sekunde|[5,Inf[:count sekundi',
+ 's' => ':count sek.',
+ 'ago' => 'prije :time',
+ 'from_now' => 'za :time',
+ 'after' => ':time nakon',
+ 'before' => ':time prije',
+
+ 'year_from_now' => '{1,21,31,41,51} :count godinu|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count godine|[5,Inf[ :count godina',
+ 'year_ago' => '{1,21,31,41,51} :count godinu|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count godine|[5,Inf[ :count godina',
+
+ 'week_from_now' => '{1} :count nedjelju|{2,3,4} :count nedjelje|[5,Inf[ :count nedjelja',
+ 'week_ago' => '{1} :count nedjelju|{2,3,4} :count nedjelje|[5,Inf[ :count nedjelja',
+
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count godine|[0,Inf[ :count godina',
+ 'y' => ':count g.',
+ 'month' => '{1} :count mjesec|{2,3,4}:count mjeseca|[5,Inf[ :count mjeseci',
+ 'm' => ':count mj.',
+ 'week' => '{1} :count nedjelja|{2,3,4}:count nedjelje|[5,Inf[ :count nedjelja',
+ 'w' => ':count ned.',
+ 'day' => '{1,21,31} :count dan|[2,Inf[ :count dana',
+ 'd' => ':count d.',
+ 'hour' => '{1,21} :count sat|{2,3,4,22,23,24}:count sata|[5,Inf[ :count sati',
+ 'h' => ':count č.',
+ 'minute' => '{1,21,31,41,51} :count minut|[2,Inf[ :count minuta',
+ 'min' => ':count min.',
+ 'second' => '{1,21,31,41,51} :count sekund|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count sekunde|[5,Inf[:count sekundi',
+ 's' => ':count sek.',
+ 'ago' => 'prije :time',
+ 'from_now' => 'za :time',
+ 'after' => ':time nakon',
+ 'before' => ':time prije',
+
+ 'year_from_now' => '{1,21,31,41,51} :count godinu|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count godine|[5,Inf[ :count godina',
+ 'year_ago' => '{1,21,31,41,51} :count godinu|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count godine|[5,Inf[ :count godina',
+
+ 'week_from_now' => '{1} :count nedjelju|{2,3,4} :count nedjelje|[5,Inf[ :count nedjelja',
+ 'week_ago' => '{1} :count nedjelju|{2,3,4} :count nedjelje|[5,Inf[ :count nedjelja',
+
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 år|:count år',
+ 'y' => '1 år|:count år',
+ 'month' => '1 månad|:count månader',
+ 'm' => '1 månad|:count månader',
+ 'week' => '1 vecka|:count veckor',
+ 'w' => '1 vecka|:count veckor',
+ 'day' => '1 dag|:count dagar',
+ 'd' => '1 dag|:count dagar',
+ 'hour' => '1 timme|:count timmar',
+ 'h' => '1 timme|:count timmar',
+ 'minute' => '1 minut|:count minuter',
+ 'min' => '1 minut|:count minuter',
+ 'second' => '1 sekund|:count sekunder',
+ 's' => '1 sekund|:count sekunder',
+ 'ago' => ':time sedan',
+ 'from_now' => 'om :time',
+ 'after' => ':time efter',
+ 'before' => ':time före',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => '1 ปี|:count ปี',
+ 'y' => '1 ปี|:count ปี',
+ 'month' => '1 เดือน|:count เดือน',
+ 'm' => '1 เดือน|:count เดือน',
+ 'week' => '1 สัปดาห์|:count สัปดาห์',
+ 'w' => '1 สัปดาห์|:count สัปดาห์',
+ 'day' => '1 วัน|:count วัน',
+ 'd' => '1 วัน|:count วัน',
+ 'hour' => '1 ชั่วโมง|:count ชั่วโมง',
+ 'h' => '1 ชั่วโมง|:count ชั่วโมง',
+ 'minute' => '1 นาที|:count นาที',
+ 'min' => '1 นาที|:count นาที',
+ 'second' => '1 วินาที|:count วินาที',
+ 's' => '1 วินาที|:count วินาที',
+ 'ago' => ':time ที่แล้ว',
+ 'from_now' => ':time จากนี้',
+ 'after' => 'หลัง:time',
+ 'before' => 'ก่อน:time',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count yıl',
+ 'y' => ':count yıl',
+ 'month' => ':count ay',
+ 'm' => ':count ay',
+ 'week' => ':count hafta',
+ 'w' => ':count hafta',
+ 'day' => ':count gün',
+ 'd' => ':count gün',
+ 'hour' => ':count saat',
+ 'h' => ':count saat',
+ 'minute' => ':count dakika',
+ 'min' => ':count dakika',
+ 'second' => ':count saniye',
+ 's' => ':count saniye',
+ 'ago' => ':time önce',
+ 'from_now' => ':time sonra',
+ 'after' => ':time sonra',
+ 'before' => ':time önce',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count рік|:count роки|:count років',
+ 'y' => ':count рік|:count роки|:count років',
+ 'month' => ':count місяць|:count місяці|:count місяців',
+ 'm' => ':count місяць|:count місяці|:count місяців',
+ 'week' => ':count тиждень|:count тижні|:count тижнів',
+ 'w' => ':count тиждень|:count тижні|:count тижнів',
+ 'day' => ':count день|:count дні|:count днів',
+ 'd' => ':count день|:count дні|:count днів',
+ 'hour' => ':count година|:count години|:count годин',
+ 'h' => ':count година|:count години|:count годин',
+ 'minute' => ':count хвилину|:count хвилини|:count хвилин',
+ 'min' => ':count хвилину|:count хвилини|:count хвилин',
+ 'second' => ':count секунду|:count секунди|:count секунд',
+ 's' => ':count секунду|:count секунди|:count секунд',
+ 'ago' => ':time назад',
+ 'from_now' => 'через :time',
+ 'after' => ':time після',
+ 'before' => ':time до',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count سال',
+ 'month' => ':count ماه',
+ 'week' => ':count ہفتے',
+ 'day' => ':count روز',
+ 'hour' => ':count گھنٹے',
+ 'minute' => ':count منٹ',
+ 'second' => ':count سیکنڈ',
+ 'ago' => ':time پہلے',
+ 'from_now' => ':time بعد',
+ 'after' => ':time بعد',
+ 'before' => ':time پہلے',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count yil|:count yil|:count yil',
+ 'y' => ':count yil|:count yil|:count yil',
+ 'month' => ':count oy|:count oy|:count oylar',
+ 'm' => ':count oy|:count oy|:count oylar',
+ 'week' => ':count hafta|:count hafta|:count hafta',
+ 'w' => ':count hafta|:count hafta|:count hafta',
+ 'day' => ':count kun|:count kun|:count kun',
+ 'd' => ':count kun|:count kun|:count kun',
+ 'hour' => ':count soat|:count soat|:count soat',
+ 'h' => ':count soat|:count soat|:count soat',
+ 'minute' => ':count minut|:count minut|:count minut',
+ 'min' => ':count minut|:count minut|:count minut',
+ 'second' => ':count sekund|:count sekund|:count sekund',
+ 's' => ':count sekund|:count sekund|:count sekund',
+ 'ago' => ':time avval',
+ 'from_now' => ':time keyin',
+ 'after' => ':time keyin',
+ 'before' => ':time oldin',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count năm',
+ 'y' => ':count năm',
+ 'month' => ':count tháng',
+ 'm' => ':count tháng',
+ 'week' => ':count tuần',
+ 'w' => ':count tuần',
+ 'day' => ':count ngày',
+ 'd' => ':count ngày',
+ 'hour' => ':count giờ',
+ 'h' => ':count giờ',
+ 'minute' => ':count phút',
+ 'min' => ':count phút',
+ 'second' => ':count giây',
+ 's' => ':count giây',
+ 'ago' => ':time trước',
+ 'from_now' => ':time từ bây giờ',
+ 'after' => ':time sau',
+ 'before' => ':time trước',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count年',
+ 'y' => ':count年',
+ 'month' => ':count个月',
+ 'm' => ':count个月',
+ 'week' => ':count周',
+ 'w' => ':count周',
+ 'day' => ':count天',
+ 'd' => ':count天',
+ 'hour' => ':count小时',
+ 'h' => ':count小时',
+ 'minute' => ':count分钟',
+ 'min' => ':count分钟',
+ 'second' => ':count秒',
+ 's' => ':count秒',
+ 'ago' => ':time前',
+ 'from_now' => '距现在:time',
+ 'after' => ':time后',
+ 'before' => ':time前',
+);
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt <brian@nesbot.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'year' => ':count 年',
+ 'y' => ':count 年',
+ 'month' => ':count 月',
+ 'm' => ':count 月',
+ 'week' => ':count 周',
+ 'w' => ':count 周',
+ 'day' => ':count 天',
+ 'd' => ':count 天',
+ 'hour' => ':count 小時',
+ 'h' => ':count 小時',
+ 'minute' => ':count 分鐘',
+ 'min' => ':count 分鐘',
+ 'second' => ':count 秒',
+ 's' => ':count 秒',
+ 'ago' => ':time前',
+ 'from_now' => '距現在 :time',
+ 'after' => ':time後',
+ 'before' => ':time前',
+);
--- /dev/null
+.travis.yml export-ignore
+package.xml export-ignore
+phpunit.xml.dist export-ignore
+php5-testingConfig.ini export-ignore
+php7-testingConfig.ini export-ignore
--- /dev/null
+/CodeSniffer.conf
+/phpcs.xml
+/phpunit.xml
+.idea/*
+/vendor/
+composer.lock
--- /dev/null
+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.
--- /dev/null
+<?php
+ $phpCodeSnifferConfig = array (
+ 'default_standard' => 'PSR2',
+ 'report_format' => 'summary',
+ 'show_warnings' => '0',
+ 'show_progress' => '1',
+ 'report_width' => '120',
+)
+?>
--- /dev/null
+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.
+
+[](https://travis-ci.org/squizlabs/PHP_CodeSniffer) [](http://squizlabs.github.io/PHP_CodeSniffer/analysis/squizlabs/PHP_CodeSniffer)
+
+[](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](http://php.net/manual/en/intro.phar.php) 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
+
+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
+
+If you prefer using [Composer](http://getcomposer.org/) you can easily 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
+
+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.
--- /dev/null
+<?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 = array();
+
+ /**
+ * A mapping of class names to file names.
+ *
+ * @var array<string, string>
+ */
+ private static $loadedFiles = array();
+
+ /**
+ * A list of additional directories to search during autoloading.
+ *
+ * This is typically a list of coding standard directories.
+ *
+ * @var string[]
+ */
+ private static $searchPaths = array();
+
+
+ /**
+ * 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
--- /dev/null
+#!/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);
--- /dev/null
+@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 "%PHPBIN%" == "" set PHPBIN=@php_bin@
+if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH
+GOTO RUN
+:USE_PEAR_PATH
+set PHPBIN=%PHP_PEAR_PHP_BIN%
+:RUN
+"%PHPBIN%" "@bin_dir@\phpcbf" %*
--- /dev/null
+#!/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);
--- /dev/null
+@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 "%PHPBIN%" == "" set PHPBIN=@php_bin@
+if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH
+GOTO RUN
+:USE_PEAR_PATH
+set PHPBIN=%PHP_PEAR_PHP_BIN%
+:RUN
+"%PHPBIN%" "@bin_dir@\phpcs" %*
--- /dev/null
+{
+ "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"
+ },
+ "bin": [
+ "bin/phpcs",
+ "bin/phpcbf"
+ ]
+}
--- /dev/null
+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.
--- /dev/null
+<?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.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"/>
+
+ <!-- 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 using string concats to stop IDEs seeing the class aliases -->
+ <rule ref="Generic.Strings.UnnecessaryStringConcat">
+ <exclude-pattern>tests/bootstrap.php</exclude-pattern>
+ </rule>
+
+</ruleset>
--- /dev/null
+#!/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
--- /dev/null
+<?php
+/**
+ * Stores the configuration used to run PHPCS and PHPCBF.
+ *
+ * Parses the command line to determine user supplied values
+ * and provides functions to access data stored in config files.
+ *
+ * @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\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Exceptions\DeepExitException;
+
+class Config
+{
+
+ /**
+ * The current version.
+ *
+ * @var string
+ */
+ const VERSION = '3.1.1';
+
+ /**
+ * Package stability; either stable, beta or alpha.
+ *
+ * @var string
+ */
+ const STABILITY = 'stable';
+
+ /**
+ * An array of settings that PHPCS and PHPCBF accept.
+ *
+ * This array is not meant to be accessed directly. Instead, use the settings
+ * as if they are class member vars so the __get() and __set() magic methods
+ * can be used to validate the values. For example, to set the verbosity level to
+ * level 2, use $this->verbosity = 2; instead of accessing this property directly.
+ *
+ * The list of settings are:
+ *
+ * string[] files The files and directories to check.
+ * string[] standards The standards being used for checking.
+ * int verbosity How verbose the output should be.
+ * 0: no unnecessary output
+ * 1: basic output for files being checked
+ * 2: ruleset and file parsing output
+ * 3: sniff execution output
+ * bool interactive Enable interactive checking mode.
+ * bool parallel Check files in parallel.
+ * bool cache Enable the use of the file cache.
+ * bool cacheFile A file where the cache data should be written
+ * bool colors Display colours in output.
+ * bool explain Explain the coding standards.
+ * bool local Process local files in directories only (no recursion).
+ * bool showSources Show sniff source codes in report output.
+ * bool showProgress Show basic progress information while running.
+ * bool quiet Quiet mode; disables progress and verbose output.
+ * bool annotations Process @codingStandard annotations.
+ * int tabWidth How many spaces each tab is worth.
+ * string encoding The encoding of the files being checked.
+ * string[] sniffs The sniffs that should be used for checking.
+ * If empty, all sniffs in the supplied standards will be used.
+ * string[] exclude The sniffs that should be excluded from checking.
+ * If empty, all sniffs in the supplied standards will be used.
+ * string[] ignored Regular expressions used to ignore files and folders during checking.
+ * string reportFile A file where the report output should be written.
+ * string generator The documentation generator to use.
+ * string filter The filter to use for the run.
+ * string[] bootstrap One of more files to include before the run begins.
+ * int reportWidth The maximum number of columns that reports should use for output.
+ * Set to "auto" for have this value changed to the width of the terminal.
+ * int errorSeverity The minimum severity an error must have to be displayed.
+ * int warningSeverity The minimum severity a warning must have to be displayed.
+ * bool recordErrors Record the content of error messages as well as error counts.
+ * string suffix A suffix to add to fixed files.
+ * string basepath A file system location to strip from the paths of files shown in reports.
+ * bool stdin Read content from STDIN instead of supplied files.
+ * string stdinContent Content passed directly to PHPCS on STDIN.
+ * string stdinPath The path to use for content passed on STDIN.
+ *
+ * array<string, string> extensions File extensions that should be checked, and what tokenizer to use.
+ * E.g., array('inc' => 'PHP');
+ * array<string, string|null> reports The reports to use for printing output after the run.
+ * The format of the array is:
+ * array(
+ * 'reportName1' => 'outputFile',
+ * 'reportName2' => null,
+ * );
+ * If the array value is NULL, the report will be written to the screen.
+ *
+ * string[] unknown Any arguments gathered on the command line that are unknown to us.
+ * E.g., using `phpcs -c` will give array('c');
+ *
+ * @var array<string, mixed>
+ */
+ private $settings = array(
+ 'files' => null,
+ 'standards' => null,
+ 'verbosity' => null,
+ 'interactive' => null,
+ 'parallel' => null,
+ 'cache' => null,
+ 'cacheFile' => null,
+ 'colors' => null,
+ 'explain' => null,
+ 'local' => null,
+ 'showSources' => null,
+ 'showProgress' => null,
+ 'quiet' => null,
+ 'annotations' => null,
+ 'tabWidth' => null,
+ 'encoding' => null,
+ 'extensions' => null,
+ 'sniffs' => null,
+ 'exclude' => null,
+ 'ignored' => null,
+ 'reportFile' => null,
+ 'generator' => null,
+ 'filter' => null,
+ 'bootstrap' => null,
+ 'reports' => null,
+ 'basepath' => null,
+ 'reportWidth' => null,
+ 'errorSeverity' => null,
+ 'warningSeverity' => null,
+ 'recordErrors' => null,
+ 'suffix' => null,
+ 'stdin' => null,
+ 'stdinContent' => null,
+ 'stdinPath' => null,
+ 'unknown' => null,
+ );
+
+ /**
+ * Whether or not to kill the process when an unknown command line arg is found.
+ *
+ * If FALSE, arguments that are not command line options or file/directory paths
+ * will be ignored and execution will continue. These values will be stored in
+ * $this->unknown.
+ *
+ * @var boolean
+ */
+ public $dieOnUnknownArg;
+
+ /**
+ * The current command line arguments we are processing.
+ *
+ * @var string[]
+ */
+ private $cliArgs = array();
+
+ /**
+ * Command line values that the user has supplied directly.
+ *
+ * @var array<string, TRUE>
+ */
+ private $overriddenDefaults = array();
+
+ /**
+ * Config file data that has been loaded for the run.
+ *
+ * @var array<string, string>
+ */
+ private static $configData = null;
+
+ /**
+ * The full path to the config data file that has been loaded.
+ *
+ * @var string
+ */
+ private static $configDataFile = null;
+
+ /**
+ * Automatically discovered executable utility paths.
+ *
+ * @var array<string, string>
+ */
+ private static $executablePaths = array();
+
+
+ /**
+ * Get the value of an inaccessible property.
+ *
+ * @param string $name The name of the property.
+ *
+ * @return mixed
+ * @throws RuntimeException If the setting name is invalid.
+ */
+ public function __get($name)
+ {
+ if (array_key_exists($name, $this->settings) === false) {
+ throw new RuntimeException("ERROR: unable to get value of property \"$name\"");
+ }
+
+ return $this->settings[$name];
+
+ }//end __get()
+
+
+ /**
+ * Set the value of an inaccessible property.
+ *
+ * @param string $name The name of the property.
+ * @param mixed $value The value of the property.
+ *
+ * @return void
+ * @throws RuntimeException If the setting name is invalid.
+ */
+ public function __set($name, $value)
+ {
+ if (array_key_exists($name, $this->settings) === false) {
+ throw new RuntimeException("Can't __set() $name; setting doesn't exist");
+ }
+
+ switch ($name) {
+ case 'reportWidth' :
+ // Support auto terminal width.
+ if ($value === 'auto' && preg_match('|\d+ (\d+)|', shell_exec('stty size 2>&1'), $matches) === 1) {
+ $value = (int) $matches[1];
+ } else {
+ $value = (int) $value;
+ }
+ break;
+ case 'standards' :
+ $cleaned = array();
+
+ // Check if the standard name is valid, or if the case is invalid.
+ $installedStandards = Util\Standards::getInstalledStandards();
+ foreach ($value as $standard) {
+ foreach ($installedStandards as $validStandard) {
+ if (strtolower($standard) === strtolower($validStandard)) {
+ $standard = $validStandard;
+ break;
+ }
+ }
+
+ $cleaned[] = $standard;
+ }
+
+ $value = $cleaned;
+ break;
+ default :
+ // No validation required.
+ break;
+ }//end switch
+
+ $this->settings[$name] = $value;
+
+ }//end __set()
+
+
+ /**
+ * Check if the value of an inaccessible property is set.
+ *
+ * @param string $name The name of the property.
+ *
+ * @return bool
+ */
+ public function __isset($name)
+ {
+ return isset($this->settings[$name]);
+
+ }//end __isset()
+
+
+ /**
+ * Unset the value of an inaccessible property.
+ *
+ * @param string $name The name of the property.
+ *
+ * @return void
+ */
+ public function __unset($name)
+ {
+ $this->settings[$name] = null;
+
+ }//end __unset()
+
+
+ /**
+ * Get the array of all config settings.
+ *
+ * @return array<string, mixed>
+ */
+ public function getSettings()
+ {
+ return $this->settings;
+
+ }//end getSettings()
+
+
+ /**
+ * Set the array of all config settings.
+ *
+ * @param array<string, mixed> $settings The array of config settings.
+ *
+ * @return void
+ */
+ public function setSettings($settings)
+ {
+ return $this->settings = $settings;
+
+ }//end setSettings()
+
+
+ /**
+ * Creates a Config object and populates it with command line values.
+ *
+ * @param array $cliArgs An array of values gathered from CLI args.
+ * @param bool $dieOnUnknownArg Whether or not to kill the process when an
+ * unknown command line arg is found.
+ *
+ * @return void
+ */
+ public function __construct(array $cliArgs=array(), $dieOnUnknownArg=true)
+ {
+ if (defined('PHP_CODESNIFFER_IN_TESTS') === true) {
+ // Let everything through during testing so that we can
+ // make use of PHPUnit command line arguments as well.
+ $this->dieOnUnknownArg = false;
+ } else {
+ $this->dieOnUnknownArg = $dieOnUnknownArg;
+ }
+
+ $checkStdin = false;
+ if (empty($cliArgs) === true) {
+ $cliArgs = $_SERVER['argv'];
+ array_shift($cliArgs);
+ $checkStdin = true;
+ }
+
+ $this->restoreDefaults();
+ $this->setCommandLineValues($cliArgs);
+
+ if (isset($this->overriddenDefaults['standards']) === false) {
+ // They did not supply a standard to use.
+ // Look for a default ruleset in the current directory or higher.
+ $currentDir = getcwd();
+
+ $defaultFiles = array(
+ '.phpcs.xml',
+ 'phpcs.xml',
+ '.phpcs.xml.dist',
+ 'phpcs.xml.dist',
+ );
+
+ do {
+ foreach ($defaultFiles as $defaultFilename) {
+ $default = $currentDir.DIRECTORY_SEPARATOR.$defaultFilename;
+ if (is_file($default) === true) {
+ $this->standards = array($default);
+ break(2);
+ }
+ }
+
+ $lastDir = $currentDir;
+ $currentDir = dirname($currentDir);
+ } while ($currentDir !== '.' && $currentDir !== $lastDir);
+ }//end if
+
+ // Check for content on STDIN.
+ if ($checkStdin === true) {
+ $handle = fopen('php://stdin', 'r');
+ if (stream_set_blocking($handle, false) === true) {
+ $fileContents = '';
+ while (($line = fgets($handle)) !== false) {
+ $fileContents .= $line;
+ usleep(10);
+ }
+
+ stream_set_blocking($handle, true);
+ fclose($handle);
+ if (trim($fileContents) !== '') {
+ $this->stdin = true;
+ $this->stdinContent = $fileContents;
+ $this->overriddenDefaults['stdin'] = true;
+ $this->overriddenDefaults['stdinContent'] = true;
+ }
+ }
+ }
+
+ }//end __construct()
+
+
+ /**
+ * Set the command line values.
+ *
+ * @param array $args An array of command line arguments to set.
+ *
+ * @return void
+ */
+ public function setCommandLineValues($args)
+ {
+ $this->cliArgs = $args;
+ $numArgs = count($args);
+
+ for ($i = 0; $i < $numArgs; $i++) {
+ $arg = $this->cliArgs[$i];
+ if ($arg === '') {
+ continue;
+ }
+
+ if ($arg{0} === '-') {
+ if ($arg === '-') {
+ // Asking to read from STDIN.
+ $this->stdin = true;
+ $this->overriddenDefaults['stdin'] = true;
+ continue;
+ }
+
+ if ($arg === '--') {
+ // Empty argument, ignore it.
+ continue;
+ }
+
+ if ($arg{1} === '-') {
+ $this->processLongArgument(substr($arg, 2), $i);
+ } else {
+ $switches = str_split($arg);
+ foreach ($switches as $switch) {
+ if ($switch === '-') {
+ continue;
+ }
+
+ $this->processShortArgument($switch, $i);
+ }
+ }
+ } else {
+ $this->processUnknownArgument($arg, $i);
+ }//end if
+ }//end for
+
+ }//end setCommandLineValues()
+
+
+ /**
+ * Restore default values for all possible command line arguments.
+ *
+ * @return array
+ */
+ public function restoreDefaults()
+ {
+ $this->files = array();
+ $this->standards = array('PEAR');
+ $this->verbosity = 0;
+ $this->interactive = false;
+ $this->cache = false;
+ $this->cacheFile = null;
+ $this->colors = false;
+ $this->explain = false;
+ $this->local = false;
+ $this->showSources = false;
+ $this->showProgress = false;
+ $this->quiet = false;
+ $this->annotations = true;
+ $this->parallel = 1;
+ $this->tabWidth = 0;
+ $this->encoding = 'utf-8';
+ $this->extensions = array(
+ 'php' => 'PHP',
+ 'inc' => 'PHP',
+ 'js' => 'JS',
+ 'css' => 'CSS',
+ );
+ $this->sniffs = array();
+ $this->exclude = array();
+ $this->ignored = array();
+ $this->reportFile = null;
+ $this->generator = null;
+ $this->filter = null;
+ $this->bootstrap = array();
+ $this->basepath = null;
+ $this->reports = array('full' => null);
+ $this->reportWidth = 'auto';
+ $this->errorSeverity = 5;
+ $this->warningSeverity = 5;
+ $this->recordErrors = true;
+ $this->suffix = '';
+ $this->stdin = false;
+ $this->stdinContent = null;
+ $this->stdinPath = null;
+ $this->unknown = array();
+
+ $standard = self::getConfigData('default_standard');
+ if ($standard !== null) {
+ $this->standards = explode(',', $standard);
+ }
+
+ $reportFormat = self::getConfigData('report_format');
+ if ($reportFormat !== null) {
+ $this->reports = array($reportFormat => null);
+ }
+
+ $tabWidth = self::getConfigData('tab_width');
+ if ($tabWidth !== null) {
+ $this->tabWidth = (int) $tabWidth;
+ }
+
+ $encoding = self::getConfigData('encoding');
+ if ($encoding !== null) {
+ $this->encoding = strtolower($encoding);
+ }
+
+ $severity = self::getConfigData('severity');
+ if ($severity !== null) {
+ $this->errorSeverity = (int) $severity;
+ $this->warningSeverity = (int) $severity;
+ }
+
+ $severity = self::getConfigData('error_severity');
+ if ($severity !== null) {
+ $this->errorSeverity = (int) $severity;
+ }
+
+ $severity = self::getConfigData('warning_severity');
+ if ($severity !== null) {
+ $this->warningSeverity = (int) $severity;
+ }
+
+ $showWarnings = self::getConfigData('show_warnings');
+ if ($showWarnings !== null) {
+ $showWarnings = (bool) $showWarnings;
+ if ($showWarnings === false) {
+ $this->warningSeverity = 0;
+ }
+ }
+
+ $reportWidth = self::getConfigData('report_width');
+ if ($reportWidth !== null) {
+ $this->reportWidth = $reportWidth;
+ }
+
+ $showProgress = self::getConfigData('show_progress');
+ if ($showProgress !== null) {
+ $this->showProgress = (bool) $showProgress;
+ }
+
+ $quiet = self::getConfigData('quiet');
+ if ($quiet !== null) {
+ $this->quiet = (bool) $quiet;
+ }
+
+ $colors = self::getConfigData('colors');
+ if ($colors !== null) {
+ $this->colors = (bool) $colors;
+ }
+
+ if (defined('PHP_CODESNIFFER_IN_TESTS') === false) {
+ $cache = self::getConfigData('cache');
+ if ($cache !== null) {
+ $this->cache = (bool) $cache;
+ }
+
+ $parallel = self::getConfigData('parallel');
+ if ($parallel !== null) {
+ $this->parallel = max((int) $parallel, 1);
+ }
+ }
+
+ }//end restoreDefaults()
+
+
+ /**
+ * Processes a short (-e) command line argument.
+ *
+ * @param string $arg The command line argument.
+ * @param int $pos The position of the argument on the command line.
+ *
+ * @return void
+ */
+ public function processShortArgument($arg, $pos)
+ {
+ switch ($arg) {
+ case 'h':
+ case '?':
+ ob_start();
+ $this->printUsage();
+ $output = ob_get_contents();
+ ob_end_clean();
+ throw new DeepExitException($output, 0);
+ case 'i' :
+ ob_start();
+ Util\Standards::printInstalledStandards();
+ $output = ob_get_contents();
+ ob_end_clean();
+ throw new DeepExitException($output, 0);
+ case 'v' :
+ if ($this->quiet === true) {
+ // Ignore when quiet mode is enabled.
+ break;
+ }
+
+ $this->verbosity++;
+ $this->overriddenDefaults['verbosity'] = true;
+ break;
+ case 'l' :
+ $this->local = true;
+ $this->overriddenDefaults['local'] = true;
+ break;
+ case 's' :
+ $this->showSources = true;
+ $this->overriddenDefaults['showSources'] = true;
+ break;
+ case 'a' :
+ $this->interactive = true;
+ $this->overriddenDefaults['interactive'] = true;
+ break;
+ case 'e':
+ $this->explain = true;
+ $this->overriddenDefaults['explain'] = true;
+ break;
+ case 'p' :
+ if ($this->quiet === true) {
+ // Ignore when quiet mode is enabled.
+ break;
+ }
+
+ $this->showProgress = true;
+ $this->overriddenDefaults['showProgress'] = true;
+ break;
+ case 'q' :
+ // Quiet mode disables a few other settings as well.
+ $this->quiet = true;
+ $this->showProgress = false;
+ $this->verbosity = 0;
+
+ $this->overriddenDefaults['quiet'] = true;
+ break;
+ case 'm' :
+ $this->recordErrors = false;
+ $this->overriddenDefaults['recordErrors'] = true;
+ break;
+ case 'd' :
+ $ini = explode('=', $this->cliArgs[($pos + 1)]);
+ $this->cliArgs[($pos + 1)] = '';
+ if (isset($ini[1]) === true) {
+ ini_set($ini[0], $ini[1]);
+ } else {
+ ini_set($ini[0], true);
+ }
+ break;
+ case 'n' :
+ if (isset($this->overriddenDefaults['warningSeverity']) === false) {
+ $this->warningSeverity = 0;
+ $this->overriddenDefaults['warningSeverity'] = true;
+ }
+ break;
+ case 'w' :
+ if (isset($this->overriddenDefaults['warningSeverity']) === false) {
+ $this->warningSeverity = $this->errorSeverity;
+ $this->overriddenDefaults['warningSeverity'] = true;
+ }
+ break;
+ default:
+ if ($this->dieOnUnknownArg === false) {
+ $unknown = $this->unknown;
+ $unknown[] = $arg;
+ $this->unknown = $unknown;
+ } else {
+ $this->processUnknownArgument('-'.$arg, $pos);
+ }
+ }//end switch
+
+ }//end processShortArgument()
+
+
+ /**
+ * Processes a long (--example) command line argument.
+ *
+ * @param string $arg The command line argument.
+ * @param int $pos The position of the argument on the command line.
+ *
+ * @return void
+ */
+ public function processLongArgument($arg, $pos)
+ {
+ switch ($arg) {
+ case 'help':
+ ob_start();
+ $this->printUsage();
+ $output = ob_get_contents();
+ ob_end_clean();
+ throw new DeepExitException($output, 0);
+ case 'version':
+ $output = 'PHP_CodeSniffer version '.self::VERSION.' ('.self::STABILITY.') ';
+ $output .= 'by Squiz (http://www.squiz.net)'.PHP_EOL;
+ throw new DeepExitException($output, 0);
+ case 'colors':
+ if (isset($this->overriddenDefaults['colors']) === true) {
+ break;
+ }
+
+ $this->colors = true;
+ $this->overriddenDefaults['colors'] = true;
+ break;
+ case 'no-colors':
+ if (isset($this->overriddenDefaults['colors']) === true) {
+ break;
+ }
+
+ $this->colors = false;
+ $this->overriddenDefaults['colors'] = true;
+ break;
+ case 'cache':
+ if (isset($this->overriddenDefaults['cache']) === true) {
+ break;
+ }
+
+ if (defined('PHP_CODESNIFFER_IN_TESTS') === false) {
+ $this->cache = true;
+ $this->overriddenDefaults['cache'] = true;
+ }
+ break;
+ case 'no-cache':
+ if (isset($this->overriddenDefaults['cache']) === true) {
+ break;
+ }
+
+ $this->cache = false;
+ $this->overriddenDefaults['cache'] = true;
+ break;
+ case 'ignore-annotations':
+ if (isset($this->overriddenDefaults['annotations']) === true) {
+ break;
+ }
+
+ $this->annotations = false;
+ $this->overriddenDefaults['annotations'] = true;
+ break;
+ case 'config-set':
+ if (isset($this->cliArgs[($pos + 1)]) === false
+ || isset($this->cliArgs[($pos + 2)]) === false
+ ) {
+ $error = 'ERROR: Setting a config option requires a name and value'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+
+ $key = $this->cliArgs[($pos + 1)];
+ $value = $this->cliArgs[($pos + 2)];
+ $current = self::getConfigData($key);
+
+ try {
+ $this->setConfigData($key, $value);
+ } catch (\Exception $e) {
+ throw new DeepExitException($e->getMessage().PHP_EOL, 3);
+ }
+
+ $output = 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
+
+ if ($current === null) {
+ $output .= "Config value \"$key\" added successfully".PHP_EOL;
+ } else {
+ $output .= "Config value \"$key\" updated successfully; old value was \"$current\"".PHP_EOL;
+ }
+ throw new DeepExitException($output, 0);
+ case 'config-delete':
+ if (isset($this->cliArgs[($pos + 1)]) === false) {
+ $error = 'ERROR: Deleting a config option requires the name of the option'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+
+ $output = 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
+
+ $key = $this->cliArgs[($pos + 1)];
+ $current = self::getConfigData($key);
+ if ($current === null) {
+ $output .= "Config value \"$key\" has not been set".PHP_EOL;
+ } else {
+ try {
+ $this->setConfigData($key, null);
+ } catch (\Exception $e) {
+ throw new DeepExitException($e->getMessage().PHP_EOL, 3);
+ }
+
+ $output .= "Config value \"$key\" removed successfully; old value was \"$current\"".PHP_EOL;
+ }
+ throw new DeepExitException($output, 0);
+ case 'config-show':
+ ob_start();
+ $data = self::getAllConfigData();
+ echo 'Using config file: '.self::$configDataFile.PHP_EOL.PHP_EOL;
+ $this->printConfigData($data);
+ $output = ob_get_contents();
+ ob_end_clean();
+ throw new DeepExitException($output, 0);
+ case 'runtime-set':
+ if (isset($this->cliArgs[($pos + 1)]) === false
+ || isset($this->cliArgs[($pos + 2)]) === false
+ ) {
+ $error = 'ERROR: Setting a runtime config option requires a name and value'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+
+ $key = $this->cliArgs[($pos + 1)];
+ $value = $this->cliArgs[($pos + 2)];
+ $this->cliArgs[($pos + 1)] = '';
+ $this->cliArgs[($pos + 2)] = '';
+ self::setConfigData($key, $value, true);
+ break;
+ default:
+ if (substr($arg, 0, 7) === 'sniffs=') {
+ if (isset($this->overriddenDefaults['sniffs']) === true) {
+ break;
+ }
+
+ $sniffs = explode(',', substr($arg, 7));
+ foreach ($sniffs as $sniff) {
+ if (substr_count($sniff, '.') !== 2) {
+ $error = 'ERROR: The specified sniff code "'.$sniff.'" is invalid'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+ }
+
+ $this->sniffs = $sniffs;
+ $this->overriddenDefaults['sniffs'] = true;
+ } else if (substr($arg, 0, 8) === 'exclude=') {
+ if (isset($this->overriddenDefaults['exclude']) === true) {
+ break;
+ }
+
+ $sniffs = explode(',', substr($arg, 8));
+ foreach ($sniffs as $sniff) {
+ if (substr_count($sniff, '.') !== 2) {
+ $error = 'ERROR: The specified sniff code "'.$sniff.'" is invalid'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+ }
+
+ $this->exclude = $sniffs;
+ $this->overriddenDefaults['exclude'] = true;
+ } else if (defined('PHP_CODESNIFFER_IN_TESTS') === false
+ && substr($arg, 0, 6) === 'cache='
+ ) {
+ if ((isset($this->overriddenDefaults['cache']) === true
+ && $this->cache === false)
+ || isset($this->overriddenDefaults['cacheFile']) === true
+ ) {
+ break;
+ }
+
+ // Turn caching on.
+ $this->cache = true;
+ $this->overriddenDefaults['cache'] = true;
+
+ $this->cacheFile = Util\Common::realpath(substr($arg, 6));
+
+ // It may not exist and return false instead.
+ if ($this->cacheFile === false) {
+ $this->cacheFile = substr($arg, 6);
+
+ $dir = dirname($this->cacheFile);
+ if (is_dir($dir) === false) {
+ $error = 'ERROR: The specified cache file path "'.$this->cacheFile.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+
+ if ($dir === '.') {
+ // Passed cache file is a file in the current directory.
+ $this->cacheFile = getcwd().'/'.basename($this->cacheFile);
+ } else {
+ if ($dir{0} === '/') {
+ // An absolute path.
+ $dir = Util\Common::realpath($dir);
+ } else {
+ $dir = Util\Common::realpath(getcwd().'/'.$dir);
+ }
+
+ if ($dir !== false) {
+ // Cache file path is relative.
+ $this->cacheFile = $dir.'/'.basename($this->cacheFile);
+ }
+ }
+ }//end if
+
+ $this->overriddenDefaults['cacheFile'] = true;
+
+ if (is_dir($this->cacheFile) === true) {
+ $error = 'ERROR: The specified cache file path "'.$this->cacheFile.'" is a directory'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+ } else if (substr($arg, 0, 10) === 'bootstrap=') {
+ $files = explode(',', substr($arg, 10));
+ $bootstrap = array();
+ foreach ($files as $file) {
+ $path = Util\Common::realpath($file);
+ if ($path === false) {
+ $error = 'ERROR: The specified bootstrap file "'.$file.'" does not exist'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+
+ $bootstrap[] = $path;
+ }
+
+ $this->bootstrap = array_merge($this->bootstrap, $bootstrap);
+ $this->overriddenDefaults['bootstrap'] = true;
+ } else if (substr($arg, 0, 10) === 'file-list=') {
+ $fileList = substr($arg, 10);
+ $path = Util\Common::realpath($fileList);
+ if ($path === false) {
+ $error = 'ERROR: The specified file list "'.$fileList.'" does not exist'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+
+ $files = file($path);
+ foreach ($files as $inputFile) {
+ $inputFile = trim($inputFile);
+
+ // Skip empty lines.
+ if ($inputFile === '') {
+ continue;
+ }
+
+ $this->processFilePath($inputFile);
+ }
+ } else if (substr($arg, 0, 11) === 'stdin-path=') {
+ if (isset($this->overriddenDefaults['stdinPath']) === true) {
+ break;
+ }
+
+ $this->stdinPath = Util\Common::realpath(substr($arg, 11));
+
+ // It may not exist and return false instead, so use whatever they gave us.
+ if ($this->stdinPath === false) {
+ $this->stdinPath = trim(substr($arg, 11));
+ }
+
+ $this->overriddenDefaults['stdinPath'] = true;
+ } else if (PHP_CODESNIFFER_CBF === false && substr($arg, 0, 12) === 'report-file=') {
+ if (isset($this->overriddenDefaults['reportFile']) === true) {
+ break;
+ }
+
+ $this->reportFile = Util\Common::realpath(substr($arg, 12));
+
+ // It may not exist and return false instead.
+ if ($this->reportFile === false) {
+ $this->reportFile = substr($arg, 12);
+
+ $dir = dirname($this->reportFile);
+ if (is_dir($dir) === false) {
+ $error = 'ERROR: The specified report file path "'.$this->reportFile.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+
+ if ($dir === '.') {
+ // Passed report file is a file in the current directory.
+ $this->reportFile = getcwd().'/'.basename($this->reportFile);
+ } else {
+ if ($dir{0} === '/') {
+ // An absolute path.
+ $dir = Util\Common::realpath($dir);
+ } else {
+ $dir = Util\Common::realpath(getcwd().'/'.$dir);
+ }
+
+ if ($dir !== false) {
+ // Report file path is relative.
+ $this->reportFile = $dir.'/'.basename($this->reportFile);
+ }
+ }
+ }//end if
+
+ $this->overriddenDefaults['reportFile'] = true;
+
+ if (is_dir($this->reportFile) === true) {
+ $error = 'ERROR: The specified report file path "'.$this->reportFile.'" is a directory'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+ } else if (substr($arg, 0, 13) === 'report-width=') {
+ if (isset($this->overriddenDefaults['reportWidth']) === true) {
+ break;
+ }
+
+ $this->reportWidth = substr($arg, 13);
+ $this->overriddenDefaults['reportWidth'] = true;
+ } else if (substr($arg, 0, 9) === 'basepath=') {
+ if (isset($this->overriddenDefaults['basepath']) === true) {
+ break;
+ }
+
+ $this->basepath = Util\Common::realpath(substr($arg, 9));
+
+ // It may not exist and return false instead.
+ if ($this->basepath === false) {
+ $this->basepath = substr($arg, 9);
+ }
+
+ $this->overriddenDefaults['basepath'] = true;
+
+ if (is_dir($this->basepath) === false) {
+ $error = 'ERROR: The specified basepath "'.$this->basepath.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+ } else if ((substr($arg, 0, 7) === 'report=' || substr($arg, 0, 7) === 'report-')) {
+ $reports = array();
+
+ if ($arg[6] === '-') {
+ // This is a report with file output.
+ $split = strpos($arg, '=');
+ if ($split === false) {
+ $report = substr($arg, 7);
+ $output = null;
+ } else {
+ $report = substr($arg, 7, ($split - 7));
+ $output = substr($arg, ($split + 1));
+ if ($output === false) {
+ $output = null;
+ } else {
+ $dir = dirname($output);
+ if ($dir === '.') {
+ // Passed report file is a filename in the current directory.
+ $output = getcwd().'/'.basename($output);
+ } else {
+ if ($dir{0} === '/') {
+ // An absolute path.
+ $dir = Util\Common::realpath($dir);
+ } else {
+ $dir = Util\Common::realpath(getcwd().'/'.$dir);
+ }
+
+ if ($dir !== false) {
+ // Report file path is relative.
+ $output = $dir.'/'.basename($output);
+ }
+ }
+ }//end if
+ }//end if
+
+ $reports[$report] = $output;
+ } else {
+ // This is a single report.
+ if (isset($this->overriddenDefaults['reports']) === true) {
+ break;
+ }
+
+ $reportNames = explode(',', substr($arg, 7));
+ foreach ($reportNames as $report) {
+ $reports[$report] = null;
+ }
+ }//end if
+
+ // Remove the default value so the CLI value overrides it.
+ if (isset($this->overriddenDefaults['reports']) === false) {
+ $this->reports = $reports;
+ } else {
+ $this->reports = array_merge($this->reports, $reports);
+ }
+
+ $this->overriddenDefaults['reports'] = true;
+ } else if (substr($arg, 0, 7) === 'filter=') {
+ if (isset($this->overriddenDefaults['filter']) === true) {
+ break;
+ }
+
+ $this->filter = substr($arg, 7);
+ $this->overriddenDefaults['filter'] = true;
+ } else if (substr($arg, 0, 9) === 'standard=') {
+ $standards = trim(substr($arg, 9));
+ if ($standards !== '') {
+ $this->standards = explode(',', $standards);
+ }
+
+ $this->overriddenDefaults['standards'] = true;
+ } else if (substr($arg, 0, 11) === 'extensions=') {
+ if (isset($this->overriddenDefaults['extensions']) === true) {
+ break;
+ }
+
+ $extensions = explode(',', substr($arg, 11));
+ $newExtensions = array();
+ foreach ($extensions as $ext) {
+ $slash = strpos($ext, '/');
+ if ($slash !== false) {
+ // They specified the tokenizer too.
+ list($ext, $tokenizer) = explode('/', $ext);
+ $newExtensions[$ext] = strtoupper($tokenizer);
+ continue;
+ }
+
+ if (isset($this->extensions[$ext]) === true) {
+ $newExtensions[$ext] = $this->extensions[$ext];
+ } else {
+ $newExtensions[$ext] = 'PHP';
+ }
+ }
+
+ $this->extensions = $newExtensions;
+ $this->overriddenDefaults['extensions'] = true;
+ } else if (substr($arg, 0, 7) === 'suffix=') {
+ if (isset($this->overriddenDefaults['suffix']) === true) {
+ break;
+ }
+
+ $this->suffix = substr($arg, 7);
+ $this->overriddenDefaults['suffix'] = true;
+ } else if (substr($arg, 0, 9) === 'parallel=') {
+ if (isset($this->overriddenDefaults['parallel']) === true) {
+ break;
+ }
+
+ $this->parallel = max((int) substr($arg, 9), 1);
+ $this->overriddenDefaults['parallel'] = true;
+ } else if (substr($arg, 0, 9) === 'severity=') {
+ $this->errorSeverity = (int) substr($arg, 9);
+ $this->warningSeverity = $this->errorSeverity;
+ if (isset($this->overriddenDefaults['errorSeverity']) === false) {
+ $this->overriddenDefaults['errorSeverity'] = true;
+ }
+
+ if (isset($this->overriddenDefaults['warningSeverity']) === false) {
+ $this->overriddenDefaults['warningSeverity'] = true;
+ }
+ } else if (substr($arg, 0, 15) === 'error-severity=') {
+ if (isset($this->overriddenDefaults['errorSeverity']) === true) {
+ break;
+ }
+
+ $this->errorSeverity = (int) substr($arg, 15);
+ $this->overriddenDefaults['errorSeverity'] = true;
+ } else if (substr($arg, 0, 17) === 'warning-severity=') {
+ if (isset($this->overriddenDefaults['warningSeverity']) === true) {
+ break;
+ }
+
+ $this->warningSeverity = (int) substr($arg, 17);
+ $this->overriddenDefaults['warningSeverity'] = true;
+ } else if (substr($arg, 0, 7) === 'ignore=') {
+ if (isset($this->overriddenDefaults['ignored']) === true) {
+ break;
+ }
+
+ // Split the ignore string on commas, unless the comma is escaped
+ // using 1 or 3 slashes (\, or \\\,).
+ $patterns = preg_split(
+ '/(?<=(?<!\\\\)\\\\\\\\),|(?<!\\\\),/',
+ substr($arg, 7)
+ );
+
+ $ignored = array();
+ foreach ($patterns as $pattern) {
+ $pattern = trim($pattern);
+ if ($pattern === '') {
+ continue;
+ }
+
+ $ignored[$pattern] = 'absolute';
+ }
+
+ $this->ignored = $ignored;
+ $this->overriddenDefaults['ignored'] = true;
+ } else if (substr($arg, 0, 10) === 'generator='
+ && PHP_CODESNIFFER_CBF === false
+ ) {
+ if (isset($this->overriddenDefaults['generator']) === true) {
+ break;
+ }
+
+ $this->generator = substr($arg, 10);
+ $this->overriddenDefaults['generator'] = true;
+ } else if (substr($arg, 0, 9) === 'encoding=') {
+ if (isset($this->overriddenDefaults['encoding']) === true) {
+ break;
+ }
+
+ $this->encoding = strtolower(substr($arg, 9));
+ $this->overriddenDefaults['encoding'] = true;
+ } else if (substr($arg, 0, 10) === 'tab-width=') {
+ if (isset($this->overriddenDefaults['tabWidth']) === true) {
+ break;
+ }
+
+ $this->tabWidth = (int) substr($arg, 10);
+ $this->overriddenDefaults['tabWidth'] = true;
+ } else {
+ if ($this->dieOnUnknownArg === false) {
+ $eqPos = strpos($arg, '=');
+ try {
+ if ($eqPos === false) {
+ $this->values[$arg] = $arg;
+ } else {
+ $value = substr($arg, ($eqPos + 1));
+ $arg = substr($arg, 0, $eqPos);
+ $this->values[$arg] = $value;
+ }
+ } catch (RuntimeException $e) {
+ // Value is not valid, so just ignore it.
+ }
+ } else {
+ $this->processUnknownArgument('--'.$arg, $pos);
+ }
+ }//end if
+
+ break;
+ }//end switch
+
+ }//end processLongArgument()
+
+
+ /**
+ * Processes an unknown command line argument.
+ *
+ * Assumes all unknown arguments are files and folders to check.
+ *
+ * @param string $arg The command line argument.
+ * @param int $pos The position of the argument on the command line.
+ *
+ * @return void
+ */
+ public function processUnknownArgument($arg, $pos)
+ {
+ // We don't know about any additional switches; just files.
+ if ($arg{0} === '-') {
+ if ($this->dieOnUnknownArg === false) {
+ return;
+ }
+
+ $error = "ERROR: option \"$arg\" not known".PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ }
+
+ $this->processFilePath($arg);
+
+ }//end processUnknownArgument()
+
+
+ /**
+ * Processes a file path and add it to the file list.
+ *
+ * @param string $path The path to the file to add.
+ *
+ * @return void
+ */
+ public function processFilePath($path)
+ {
+ // If we are processing STDIN, don't record any files to check.
+ if ($this->stdin === true) {
+ return;
+ }
+
+ $file = Util\Common::realpath($path);
+ if (file_exists($file) === false) {
+ if ($this->dieOnUnknownArg === false) {
+ return;
+ }
+
+ $error = 'ERROR: The file "'.$path.'" does not exist.'.PHP_EOL.PHP_EOL;
+ $error .= $this->printShortUsage(true);
+ throw new DeepExitException($error, 3);
+ } else {
+ $files = $this->files;
+ $files[] = $file;
+ $this->files = $files;
+ $this->overriddenDefaults['files'] = true;
+ }
+
+ }//end processFilePath()
+
+
+ /**
+ * Prints out the usage information for this script.
+ *
+ * @return void
+ */
+ public function printUsage()
+ {
+ echo PHP_EOL;
+
+ if (PHP_CODESNIFFER_CBF === true) {
+ $this->printPHPCBFUsage();
+ } else {
+ $this->printPHPCSUsage();
+ }
+
+ echo PHP_EOL;
+
+ }//end printUsage()
+
+
+ /**
+ * Prints out the short usage information for this script.
+ *
+ * @param bool $return If TRUE, the usage string is returned
+ * instead of output to screen.
+ *
+ * @return string|void
+ */
+ public function printShortUsage($return=false)
+ {
+ if (PHP_CODESNIFFER_CBF === true) {
+ $usage = 'Run "phpcbf --help" for usage information';
+ } else {
+ $usage = 'Run "phpcs --help" for usage information';
+ }
+
+ $usage .= PHP_EOL.PHP_EOL;
+
+ if ($return === true) {
+ return $usage;
+ }
+
+ echo $usage;
+
+ }//end printShortUsage()
+
+
+ /**
+ * Prints out the usage information for PHPCS.
+ *
+ * @return void
+ */
+ public function printPHPCSUsage()
+ {
+ echo 'Usage: phpcs [-nwlsaepqvi] [-d key[=value]] [--colors] [--no-colors]'.PHP_EOL;
+ echo ' [--cache[=<cacheFile>]] [--no-cache] [--tab-width=<tabWidth>]'.PHP_EOL;
+ echo ' [--report=<report>] [--report-file=<reportFile>] [--report-<report>=<reportFile>]'.PHP_EOL;
+ echo ' [--report-width=<reportWidth>] [--basepath=<basepath>] [--bootstrap=<bootstrap>]'.PHP_EOL;
+ echo ' [--severity=<severity>] [--error-severity=<severity>] [--warning-severity=<severity>]'.PHP_EOL;
+ echo ' [--runtime-set key value] [--config-set key value] [--config-delete key] [--config-show]'.PHP_EOL;
+ echo ' [--standard=<standard>] [--sniffs=<sniffs>] [--exclude=<sniffs>]'.PHP_EOL;
+ echo ' [--encoding=<encoding>] [--parallel=<processes>] [--generator=<generator>]'.PHP_EOL;
+ echo ' [--extensions=<extensions>] [--ignore=<patterns>] [--ignore-annotations]'.PHP_EOL;
+ echo ' [--stdin-path=<stdinPath>] [--file-list=<fileList>] <file> - ...'.PHP_EOL;
+ echo PHP_EOL;
+ echo ' - Check STDIN instead of local files and directories'.PHP_EOL;
+ echo ' -n Do not print warnings (shortcut for --warning-severity=0)'.PHP_EOL;
+ echo ' -w Print both warnings and errors (this is the default)'.PHP_EOL;
+ echo ' -l Local directory only, no recursion'.PHP_EOL;
+ echo ' -s Show sniff codes in all reports'.PHP_EOL;
+ echo ' -a Run interactively'.PHP_EOL;
+ echo ' -e Explain a standard by showing the sniffs it includes'.PHP_EOL;
+ echo ' -p Show progress of the run'.PHP_EOL;
+ echo ' -q Quiet mode; disables progress and verbose output'.PHP_EOL;
+ echo ' -m Stop error messages from being recorded'.PHP_EOL;
+ echo ' (saves a lot of memory, but stops many reports from being used)'.PHP_EOL;
+ echo ' -v Print processed files'.PHP_EOL;
+ echo ' -vv Print ruleset and token output'.PHP_EOL;
+ echo ' -vvv Print sniff processing information'.PHP_EOL;
+ echo ' -i Show a list of installed coding standards'.PHP_EOL;
+ echo ' -d Set the [key] php.ini value to [value] or [true] if value is omitted'.PHP_EOL;
+ echo PHP_EOL;
+ echo ' --help Print this help message'.PHP_EOL;
+ echo ' --version Print version information'.PHP_EOL;
+ echo ' --colors Use colors in output'.PHP_EOL;
+ echo ' --no-colors Do not use colors in output (this is the default)'.PHP_EOL;
+ echo ' --cache Cache results between runs'.PHP_EOL;
+ echo ' --no-cache Do not cache results between runs (this is the default)'.PHP_EOL;
+ echo ' --ignore-annotations Ignore all @codingStandard annotations in code comments'.PHP_EOL;
+ echo PHP_EOL;
+ echo ' <cacheFile> Use a specific file for caching (uses a temporary file by default)'.PHP_EOL;
+ echo ' <basepath> A path to strip from the front of file paths inside reports'.PHP_EOL;
+ echo ' <bootstrap> A comma separated list of files to run before processing begins'.PHP_EOL;
+ echo ' <file> One or more files and/or directories to check'.PHP_EOL;
+ echo ' <fileList> A file containing a list of files and/or directories to check (one per line)'.PHP_EOL;
+ echo ' <encoding> The encoding of the files being checked (default is utf-8)'.PHP_EOL;
+ echo ' <extensions> A comma separated list of file extensions to check'.PHP_EOL;
+ echo ' The type of the file can be specified using: ext/type'.PHP_EOL;
+ echo ' e.g., module/php,es/js'.PHP_EOL;
+ echo ' <generator> Uses either the "HTML", "Markdown" or "Text" generator'.PHP_EOL;
+ echo ' (forces documentation generation instead of checking)'.PHP_EOL;
+ echo ' <patterns> A comma separated list of patterns to ignore files and directories'.PHP_EOL;
+ echo ' <processes> How many files should be checked simultaneously (default is 1)'.PHP_EOL;
+ echo ' <report> Print either the "full", "xml", "checkstyle", "csv"'.PHP_EOL;
+ echo ' "json", "junit", "emacs", "source", "summary", "diff"'.PHP_EOL;
+ echo ' "svnblame", "gitblame", "hgblame" or "notifysend" report'.PHP_EOL;
+ echo ' (the "full" report is printed by default)'.PHP_EOL;
+ echo ' <reportFile> Write the report to the specified file path'.PHP_EOL;
+ echo ' <reportWidth> How many columns wide screen reports should be printed'.PHP_EOL;
+ echo ' or set to "auto" to use current screen width, where supported'.PHP_EOL;
+ echo ' <severity> The minimum severity required to display an error or warning'.PHP_EOL;
+ echo ' <sniffs> A comma separated list of sniff codes to include or exclude from checking'.PHP_EOL;
+ echo ' (all sniffs must be part of the specified standard)'.PHP_EOL;
+ echo ' <standard> The name or path of the coding standard to use'.PHP_EOL;
+ echo ' <stdinPath> If processing STDIN, the file path that STDIN will be processed as'.PHP_EOL;
+ echo ' <tabWidth> The number of spaces each tab represents'.PHP_EOL;
+
+ }//end printPHPCSUsage()
+
+
+ /**
+ * Prints out the usage information for PHPCBF.
+ *
+ * @return void
+ */
+ public function printPHPCBFUsage()
+ {
+ echo 'Usage: phpcbf [-nwli] [-d key[=value]] [--ignore-annotations] [--bootstrap=<bootstrap>]'.PHP_EOL;
+ echo ' [--standard=<standard>] [--sniffs=<sniffs>] [--exclude=<sniffs>] [--suffix=<suffix>]'.PHP_EOL;
+ echo ' [--severity=<severity>] [--error-severity=<severity>] [--warning-severity=<severity>]'.PHP_EOL;
+ echo ' [--tab-width=<tabWidth>] [--encoding=<encoding>] [--parallel=<processes>]'.PHP_EOL;
+ echo ' [--basepath=<basepath>] [--extensions=<extensions>] [--ignore=<patterns>]'.PHP_EOL;
+ echo ' [--stdin-path=<stdinPath>] [--file-list=<fileList>] <file> - ...'.PHP_EOL;
+ echo PHP_EOL;
+ echo ' - Fix STDIN instead of local files and directories'.PHP_EOL;
+ echo ' -n Do not fix warnings (shortcut for --warning-severity=0)'.PHP_EOL;
+ echo ' -w Fix both warnings and errors (on by default)'.PHP_EOL;
+ echo ' -l Local directory only, no recursion'.PHP_EOL;
+ echo ' -p Show progress of the run'.PHP_EOL;
+ echo ' -q Quiet mode; disables progress and verbose output'.PHP_EOL;
+ echo ' -v Print processed files'.PHP_EOL;
+ echo ' -vv Print ruleset and token output'.PHP_EOL;
+ echo ' -vvv Print sniff processing information'.PHP_EOL;
+ echo ' -i Show a list of installed coding standards'.PHP_EOL;
+ echo ' -d Set the [key] php.ini value to [value] or [true] if value is omitted'.PHP_EOL;
+ echo PHP_EOL;
+ echo ' --help Print this help message'.PHP_EOL;
+ echo ' --version Print version information'.PHP_EOL;
+ echo ' --ignore-annotations Ignore all @codingStandard annotations in code comments'.PHP_EOL;
+ echo PHP_EOL;
+ echo ' <basepath> A path to strip from the front of file paths inside reports'.PHP_EOL;
+ echo ' <bootstrap> A comma separated list of files to run before processing begins'.PHP_EOL;
+ echo ' <file> One or more files and/or directories to fix'.PHP_EOL;
+ echo ' <fileList> A file containing a list of files and/or directories to fix (one per line)'.PHP_EOL;
+ echo ' <encoding> The encoding of the files being fixed (default is utf-8)'.PHP_EOL;
+ echo ' <extensions> A comma separated list of file extensions to fix'.PHP_EOL;
+ echo ' The type of the file can be specified using: ext/type'.PHP_EOL;
+ echo ' e.g., module/php,es/js'.PHP_EOL;
+ echo ' <patterns> A comma separated list of patterns to ignore files and directories'.PHP_EOL;
+ echo ' <processes> How many files should be fixed simultaneously (default is 1)'.PHP_EOL;
+ echo ' <severity> The minimum severity required to fix an error or warning'.PHP_EOL;
+ echo ' <sniffs> A comma separated list of sniff codes to include or exclude from fixing'.PHP_EOL;
+ echo ' (all sniffs must be part of the specified standard)'.PHP_EOL;
+ echo ' <standard> The name or path of the coding standard to use'.PHP_EOL;
+ echo ' <stdinPath> If processing STDIN, the file path that STDIN will be processed as'.PHP_EOL;
+ echo ' <suffix> Write modified files to a filename using this suffix'.PHP_EOL;
+ echo ' ("diff" and "patch" are not used in this mode)'.PHP_EOL;
+ echo ' <tabWidth> The number of spaces each tab represents'.PHP_EOL;
+
+ }//end printPHPCBFUsage()
+
+
+ /**
+ * Get a single config value.
+ *
+ * @param string $key The name of the config value.
+ *
+ * @return string|null
+ * @see setConfigData()
+ * @see getAllConfigData()
+ */
+ public static function getConfigData($key)
+ {
+ $phpCodeSnifferConfig = self::getAllConfigData();
+
+ if ($phpCodeSnifferConfig === null) {
+ return null;
+ }
+
+ if (isset($phpCodeSnifferConfig[$key]) === false) {
+ return null;
+ }
+
+ return $phpCodeSnifferConfig[$key];
+
+ }//end getConfigData()
+
+
+ /**
+ * Get the path to an executable utility.
+ *
+ * @param string $name The name of the executable utility.
+ *
+ * @return string|null
+ * @see getConfigData()
+ */
+ public static function getExecutablePath($name)
+ {
+ $data = self::getConfigData($name.'_path');
+ if ($data !== null) {
+ return $data;
+ }
+
+ if (array_key_exists($name, self::$executablePaths) === true) {
+ return self::$executablePaths[$name];
+ }
+
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+ $cmd = 'where '.escapeshellarg($name).' 2> nul';
+ } else {
+ $cmd = 'which '.escapeshellarg($name).' 2> /dev/null';
+ }
+
+ $result = exec($cmd, $output, $retVal);
+ if ($retVal !== 0) {
+ $result = null;
+ }
+
+ self::$executablePaths[$name] = $result;
+ return $result;
+
+ }//end getExecutablePath()
+
+
+ /**
+ * Set a single config value.
+ *
+ * @param string $key The name of the config value.
+ * @param string|null $value The value to set. If null, the config
+ * entry is deleted, reverting it to the
+ * default value.
+ * @param boolean $temp Set this config data temporarily for this
+ * script run. This will not write the config
+ * data to the config file.
+ *
+ * @return bool
+ * @see getConfigData()
+ * @throws RuntimeException If the config file can not be written.
+ */
+ public static function setConfigData($key, $value, $temp=false)
+ {
+ if ($temp === false) {
+ $path = '';
+ if (is_callable('\Phar::running') === true) {
+ $path = \Phar::running(false);
+ }
+
+ if ($path !== '') {
+ $configFile = dirname($path).'/CodeSniffer.conf';
+ } else {
+ $configFile = dirname(__DIR__).'/CodeSniffer.conf';
+ if (is_file($configFile) === false
+ && strpos('@data_dir@', '@data_dir') === false
+ ) {
+ // If data_dir was replaced, this is a PEAR install and we can
+ // use the PEAR data dir to store the conf file.
+ $configFile = '@data_dir@/PHP_CodeSniffer/CodeSniffer.conf';
+ }
+ }
+
+ if (is_file($configFile) === true
+ && is_writable($configFile) === false
+ ) {
+ $error = 'ERROR: Config file '.$configFile.' is not writable'.PHP_EOL.PHP_EOL;
+ throw new DeepExitException($error, 3);
+ }
+ }//end if
+
+ $phpCodeSnifferConfig = self::getAllConfigData();
+
+ if ($value === null) {
+ if (isset($phpCodeSnifferConfig[$key]) === true) {
+ unset($phpCodeSnifferConfig[$key]);
+ }
+ } else {
+ $phpCodeSnifferConfig[$key] = $value;
+ }
+
+ if ($temp === false) {
+ $output = '<'.'?php'."\n".' $phpCodeSnifferConfig = ';
+ $output .= var_export($phpCodeSnifferConfig, true);
+ $output .= "\n?".'>';
+
+ if (file_put_contents($configFile, $output) === false) {
+ $error = 'ERROR: Config file '.$configFile.' could not be written'.PHP_EOL.PHP_EOL;
+ throw new DeepExitException($error, 3);
+ }
+
+ self::$configDataFile = $configFile;
+ }
+
+ self::$configData = $phpCodeSnifferConfig;
+
+ // If the installed paths are being set, make sure all known
+ // standards paths are added to the autoloader.
+ if ($key === 'installed_paths') {
+ $installedStandards = Util\Standards::getInstalledStandardDetails();
+ foreach ($installedStandards as $name => $details) {
+ Autoload::addSearchPath($details['path'], $details['namespace']);
+ }
+ }
+
+ return true;
+
+ }//end setConfigData()
+
+
+ /**
+ * Get all config data.
+ *
+ * @return array<string, string>
+ * @see getConfigData()
+ */
+ public static function getAllConfigData()
+ {
+ if (self::$configData !== null) {
+ return self::$configData;
+ }
+
+ $path = '';
+ if (is_callable('\Phar::running') === true) {
+ $path = \Phar::running(false);
+ }
+
+ if ($path !== '') {
+ $configFile = dirname($path).'/CodeSniffer.conf';
+ } else {
+ $configFile = dirname(__DIR__).'/CodeSniffer.conf';
+ if (is_file($configFile) === false
+ && strpos('@data_dir@', '@data_dir') === false
+ ) {
+ $configFile = '@data_dir@/PHP_CodeSniffer/CodeSniffer.conf';
+ }
+ }
+
+ if (is_file($configFile) === false) {
+ self::$configData = array();
+ return array();
+ }
+
+ if (is_readable($configFile) === false) {
+ $error = 'ERROR: Config file '.$configFile.' is not readable'.PHP_EOL.PHP_EOL;
+ throw new DeepExitException($error, 3);
+ }
+
+ include $configFile;
+ self::$configDataFile = $configFile;
+ self::$configData = $phpCodeSnifferConfig;
+ return self::$configData;
+
+ }//end getAllConfigData()
+
+
+ /**
+ * Prints out the gathered config data.
+ *
+ * @param array $data The config data to print.
+ *
+ * @return void
+ */
+ public function printConfigData($data)
+ {
+ $max = 0;
+ $keys = array_keys($data);
+ foreach ($keys as $key) {
+ $len = strlen($key);
+ if (strlen($key) > $max) {
+ $max = $len;
+ }
+ }
+
+ if ($max === 0) {
+ return;
+ }
+
+ $max += 2;
+ ksort($data);
+ foreach ($data as $name => $value) {
+ echo str_pad($name.': ', $max).$value.PHP_EOL;
+ }
+
+ }//end printConfigData()
+
+
+}//end class
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?php
+/**
+ * Represents a piece of content being checked during the 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\Files;
+
+use PHP_CodeSniffer\Ruleset;
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Fixer;
+use PHP_CodeSniffer\Util;
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Exceptions\TokenizerException;
+
+class File
+{
+
+ /**
+ * The absolute path to the file associated with this object.
+ *
+ * @var string
+ */
+ public $path = '';
+
+ /**
+ * The absolute path to the file associated with this object.
+ *
+ * @var string
+ */
+ protected $content = '';
+
+ /**
+ * 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;
+
+ /**
+ * If TRUE, the entire file is being ignored.
+ *
+ * @var string
+ */
+ public $ignored = false;
+
+ /**
+ * The EOL character this file uses.
+ *
+ * @var string
+ */
+ public $eolChar = '';
+
+ /**
+ * The Fixer object to control fixing errors.
+ *
+ * @var \PHP_CodeSniffer\Fixer
+ */
+ public $fixer = null;
+
+ /**
+ * The tokenizer being used for this file.
+ *
+ * @var \PHP_CodeSniffer\Tokenizers\Tokenizer
+ */
+ public $tokenizer = null;
+
+ /**
+ * Was the file loaded from cache?
+ *
+ * If TRUE, the file was loaded from a local cache.
+ * If FALSE, the file was tokenized and processed fully.
+ *
+ * @var boolean
+ */
+ public $fromCache = false;
+
+ /**
+ * The number of tokens in this file.
+ *
+ * Stored here to save calling count() everywhere.
+ *
+ * @var integer
+ */
+ public $numTokens = 0;
+
+ /**
+ * The tokens stack map.
+ *
+ * @var array
+ */
+ protected $tokens = array();
+
+ /**
+ * The errors raised from sniffs.
+ *
+ * @var array
+ * @see getErrors()
+ */
+ protected $errors = array();
+
+ /**
+ * The warnings raised from sniffs.
+ *
+ * @var array
+ * @see getWarnings()
+ */
+ protected $warnings = array();
+
+ /**
+ * The metrics recorded by sniffs.
+ *
+ * @var array
+ * @see getMetrics()
+ */
+ protected $metrics = array();
+
+ /**
+ * The total number of errors raised.
+ *
+ * @var integer
+ */
+ protected $errorCount = 0;
+
+ /**
+ * The total number of warnings raised.
+ *
+ * @var integer
+ */
+ protected $warningCount = 0;
+
+ /**
+ * The total number of errors and warnings that can be fixed.
+ *
+ * @var integer
+ */
+ protected $fixableCount = 0;
+
+ /**
+ * The total number of errors and warnings that were fixed.
+ *
+ * @var integer
+ */
+ protected $fixedCount = 0;
+
+ /**
+ * An array of sniffs that are being ignored.
+ *
+ * @var array
+ */
+ protected $ignoredListeners = array();
+
+ /**
+ * An array of message codes that are being ignored.
+ *
+ * @var array
+ */
+ protected $ignoredCodes = array();
+
+ /**
+ * An array of sniffs listening to this file's processing.
+ *
+ * @var \PHP_CodeSniffer\Sniffs\Sniff[]
+ */
+ protected $listeners = array();
+
+ /**
+ * The class name of the sniff currently processing the file.
+ *
+ * @var string
+ */
+ protected $activeListener = '';
+
+ /**
+ * An array of sniffs being processed and how long they took.
+ *
+ * @var array
+ */
+ protected $listenerTimes = array();
+
+ /**
+ * A cache of often used config settings to improve performance.
+ *
+ * Storing them here saves 10k+ calls to __get() in the Config class.
+ *
+ * @var array
+ */
+ protected $configCache = array();
+
+
+ /**
+ * Constructs a file.
+ *
+ * @param string $path The absolute path to the file to process.
+ * @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 = $path;
+ $this->ruleset = $ruleset;
+ $this->config = $config;
+ $this->fixer = new Fixer();
+
+ $parts = explode('.', $path);
+ $extension = array_pop($parts);
+ if (isset($config->extensions[$extension]) === true) {
+ $this->tokenizerType = $config->extensions[$extension];
+ } else {
+ // Revert to default.
+ $this->tokenizerType = 'PHP';
+ }
+
+ $this->configCache['cache'] = $this->config->cache;
+ $this->configCache['sniffs'] = array_map('strtolower', $this->config->sniffs);
+ $this->configCache['exclude'] = array_map('strtolower', $this->config->exclude);
+ $this->configCache['errorSeverity'] = $this->config->errorSeverity;
+ $this->configCache['warningSeverity'] = $this->config->warningSeverity;
+ $this->configCache['recordErrors'] = $this->config->recordErrors;
+ $this->configCache['ignorePatterns'] = $this->ruleset->getIgnorePatterns();
+
+ }//end __construct()
+
+
+ /**
+ * Set the content of the file.
+ *
+ * Setting the content also calculates the EOL char being used.
+ *
+ * @param string $content The file content.
+ *
+ * @return void
+ */
+ public function setContent($content)
+ {
+ $this->content = $content;
+ $this->tokens = array();
+
+ try {
+ $this->eolChar = Util\Common::detectLineEndings($content);
+ } catch (RuntimeException $e) {
+ $this->addWarningOnLine($e->getMessage(), 1, 'Internal.DetectLineEndings');
+ return;
+ }
+
+ }//end setContent()
+
+
+ /**
+ * Reloads the content of the file.
+ *
+ * By default, we have no idea where our content comes from,
+ * so we can't do anything.
+ *
+ * @return void
+ */
+ public function reloadContent()
+ {
+
+ }//end reloadContent()
+
+
+ /**
+ * Disables caching of this file.
+ *
+ * @return void
+ */
+ public function disableCaching()
+ {
+ $this->configCache['cache'] = false;
+
+ }//end disableCaching()
+
+
+ /**
+ * Starts the stack traversal and tells listeners when tokens are found.
+ *
+ * @return void
+ */
+ public function process()
+ {
+ if ($this->ignored === true) {
+ return;
+ }
+
+ $this->errors = array();
+ $this->warnings = array();
+ $this->errorCount = 0;
+ $this->warningCount = 0;
+ $this->fixableCount = 0;
+
+ $this->parse();
+
+ $this->fixer->startFile($this);
+
+ if (PHP_CODESNIFFER_VERBOSITY > 2) {
+ echo "\t*** START TOKEN PROCESSING ***".PHP_EOL;
+ }
+
+ $foundCode = false;
+ $listenerIgnoreTo = array();
+ $inTests = defined('PHP_CODESNIFFER_IN_TESTS');
+ $checkAnnotations = $this->config->annotations;
+
+ // Foreach of the listeners that have registered to listen for this
+ // token, get them to process it.
+ foreach ($this->tokens as $stackPtr => $token) {
+ // Check for ignored lines.
+ if ($checkAnnotations === true
+ && ($token['code'] === T_COMMENT
+ || $token['code'] === T_DOC_COMMENT_TAG
+ || ($inTests === true && $token['code'] === T_INLINE_HTML))
+ ) {
+ if (strpos($token['content'], '@codingStandards') !== false) {
+ if (strpos($token['content'], '@codingStandardsIgnoreFile') !== false) {
+ // Ignoring the whole file, just a little late.
+ $this->errors = array();
+ $this->warnings = array();
+ $this->errorCount = 0;
+ $this->warningCount = 0;
+ $this->fixableCount = 0;
+ return;
+ } else if (strpos($token['content'], '@codingStandardsChangeSetting') !== false) {
+ $start = strpos($token['content'], '@codingStandardsChangeSetting');
+ $comment = substr($token['content'], ($start + 30));
+ $parts = explode(' ', $comment);
+ if ($parts >= 3) {
+ $sniffParts = explode('.', $parts[0]);
+ if ($sniffParts >= 3) {
+ // If the sniff code is not know to us, it has not been registered in this run.
+ // But don't throw an error as it could be there for a different standard to use.
+ if (isset($this->ruleset->sniffCodes[$parts[0]]) === true) {
+ $listenerCode = array_shift($parts);
+ $propertyCode = array_shift($parts);
+ $propertyValue = rtrim(implode(' ', $parts), " */\r\n");
+ $listenerClass = $this->ruleset->sniffCodes[$listenerCode];
+ $this->ruleset->setSniffProperty($listenerClass, $propertyCode, $propertyValue);
+ }
+ }
+ }
+ }//end if
+ }//end if
+ }//end if
+
+ if (PHP_CODESNIFFER_VERBOSITY > 2) {
+ $type = $token['type'];
+ $content = Util\Common::prepareForOutput($token['content']);
+ echo "\t\tProcess token $stackPtr: $type => $content".PHP_EOL;
+ }
+
+ if ($token['code'] !== T_INLINE_HTML) {
+ $foundCode = true;
+ }
+
+ if (isset($this->ruleset->tokenListeners[$token['code']]) === false) {
+ continue;
+ }
+
+ foreach ($this->ruleset->tokenListeners[$token['code']] as $listenerData) {
+ if (isset($this->ignoredListeners[$listenerData['class']]) === true
+ || (isset($listenerIgnoreTo[$listenerData['class']]) === true
+ && $listenerIgnoreTo[$listenerData['class']] > $stackPtr)
+ ) {
+ // This sniff is ignoring past this token, or the whole file.
+ continue;
+ }
+
+ // Make sure this sniff supports the tokenizer
+ // we are currently using.
+ $class = $listenerData['class'];
+
+ if (isset($listenerData['tokenizers'][$this->tokenizerType]) === false) {
+ continue;
+ }
+
+ // If the file path matches one of our ignore patterns, skip it.
+ // While there is support for a type of each pattern
+ // (absolute or relative) we don't actually support it here.
+ foreach ($listenerData['ignore'] as $pattern) {
+ // 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 === '\\') {
+ $pattern = str_replace('/', '\\\\', $pattern);
+ }
+
+ $pattern = '`'.$pattern.'`i';
+ if (preg_match($pattern, $this->path) === 1) {
+ $this->ignoredListeners[$class] = true;
+ continue(2);
+ }
+ }
+
+ // If the file path does not match one of our include patterns, skip it.
+ // While there is support for a type of each pattern
+ // (absolute or relative) we don't actually support it here.
+ if (empty($listenerData['include']) === false) {
+ $included = false;
+ foreach ($listenerData['include'] as $pattern) {
+ // 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 === '\\') {
+ $pattern = str_replace('/', '\\\\', $pattern);
+ }
+
+ $pattern = '`'.$pattern.'`i';
+ if (preg_match($pattern, $this->path) === 1) {
+ $included = true;
+ break;
+ }
+ }
+
+ if ($included === false) {
+ $this->ignoredListeners[$class] = true;
+ continue;
+ }
+ }//end if
+
+ $this->activeListener = $class;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 2) {
+ $startTime = microtime(true);
+ echo "\t\t\tProcessing ".$this->activeListener.'... ';
+ }
+
+ $ignoreTo = $this->ruleset->sniffs[$class]->process($this, $stackPtr);
+ if ($ignoreTo !== null) {
+ $listenerIgnoreTo[$this->activeListener] = $ignoreTo;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 2) {
+ $timeTaken = (microtime(true) - $startTime);
+ if (isset($this->listenerTimes[$this->activeListener]) === false) {
+ $this->listenerTimes[$this->activeListener] = 0;
+ }
+
+ $this->listenerTimes[$this->activeListener] += $timeTaken;
+
+ $timeTaken = round(($timeTaken), 4);
+ echo "DONE in $timeTaken seconds".PHP_EOL;
+ }
+
+ $this->activeListener = '';
+ }//end foreach
+ }//end foreach
+
+ // If short open tags are off but the file being checked uses
+ // short open tags, the whole content will be inline HTML
+ // and nothing will be checked. So try and handle this case.
+ // We don't show this error for STDIN because we can't be sure the content
+ // actually came directly from the user. It could be something like
+ // refs from a Git pre-push hook.
+ if ($foundCode === false && $this->tokenizerType === 'PHP' && $this->path !== 'STDIN') {
+ $shortTags = (bool) ini_get('short_open_tag');
+ if ($shortTags === false) {
+ $error = 'No PHP code was found in this file and short open tags are not allowed by this install of PHP. This file may be using short open tags but PHP does not allow them.';
+ $this->addWarning($error, null, 'Internal.NoCodeFound');
+ }
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 2) {
+ echo "\t*** END TOKEN PROCESSING ***".PHP_EOL;
+ echo "\t*** START SNIFF PROCESSING REPORT ***".PHP_EOL;
+
+ asort($this->listenerTimes, SORT_NUMERIC);
+ $this->listenerTimes = array_reverse($this->listenerTimes, true);
+ foreach ($this->listenerTimes as $listener => $timeTaken) {
+ echo "\t$listener: ".round(($timeTaken), 4).' secs'.PHP_EOL;
+ }
+
+ echo "\t*** END SNIFF PROCESSING REPORT ***".PHP_EOL;
+ }
+
+ $this->fixedCount += $this->fixer->getFixCount();
+
+ }//end process()
+
+
+ /**
+ * Tokenizes the file and prepares it for the test run.
+ *
+ * @return void
+ */
+ public function parse()
+ {
+ if (empty($this->tokens) === false) {
+ // File has already been parsed.
+ return;
+ }
+
+ try {
+ $tokenizerClass = 'PHP_CodeSniffer\Tokenizers\\'.$this->tokenizerType;
+ $this->tokenizer = new $tokenizerClass($this->content, $this->config, $this->eolChar);
+ $this->tokens = $this->tokenizer->getTokens();
+ } catch (TokenizerException $e) {
+ $this->addWarning($e->getMessage(), null, 'Internal.Tokenizer.Exception');
+ if (PHP_CODESNIFFER_VERBOSITY > 0) {
+ echo "[$this->tokenizerType => tokenizer error]... ";
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo PHP_EOL;
+ }
+ }
+
+ return;
+ }
+
+ $this->numTokens = count($this->tokens);
+
+ // Check for mixed line endings as these can cause tokenizer errors and we
+ // should let the user know that the results they get may be incorrect.
+ // This is done by removing all backslashes, removing the newline char we
+ // detected, then converting newlines chars into text. If any backslashes
+ // are left at the end, we have additional newline chars in use.
+ $contents = str_replace('\\', '', $this->content);
+ $contents = str_replace($this->eolChar, '', $contents);
+ $contents = str_replace("\n", '\n', $contents);
+ $contents = str_replace("\r", '\r', $contents);
+ if (strpos($contents, '\\') !== false) {
+ $error = 'File has mixed line endings; this may cause incorrect results';
+ $this->addWarningOnLine($error, 1, 'Internal.LineEndings.Mixed');
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 0) {
+ if ($this->numTokens === 0) {
+ $numLines = 0;
+ } else {
+ $numLines = $this->tokens[($this->numTokens - 1)]['line'];
+ }
+
+ echo "[$this->tokenizerType => $this->numTokens tokens in $numLines lines]... ";
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo PHP_EOL;
+ }
+ }
+
+ }//end parse()
+
+
+ /**
+ * Returns the token stack for this file.
+ *
+ * @return array
+ */
+ public function getTokens()
+ {
+ return $this->tokens;
+
+ }//end getTokens()
+
+
+ /**
+ * Remove vars stored in this file that are no longer required.
+ *
+ * @return void
+ */
+ public function cleanUp()
+ {
+ $this->listenerTimes = null;
+ $this->content = null;
+ $this->tokens = null;
+ $this->tokenizer = null;
+ $this->fixer = null;
+ $this->config = null;
+ $this->ruleset = null;
+
+ }//end cleanUp()
+
+
+ /**
+ * Records an error against a specific token in the file.
+ *
+ * @param string $error The error message.
+ * @param int $stackPtr The stack position where the error occurred.
+ * @param string $code A violation code unique to the sniff message.
+ * @param array $data Replacements for the error message.
+ * @param int $severity The severity level for this error. A value of 0
+ * will be converted into the default severity level.
+ * @param boolean $fixable Can the error be fixed by the sniff?
+ *
+ * @return boolean
+ */
+ public function addError(
+ $error,
+ $stackPtr,
+ $code,
+ $data=array(),
+ $severity=0,
+ $fixable=false
+ ) {
+ if ($stackPtr === null) {
+ $line = 1;
+ $column = 1;
+ } else {
+ $line = $this->tokens[$stackPtr]['line'];
+ $column = $this->tokens[$stackPtr]['column'];
+ }
+
+ return $this->addMessage(true, $error, $line, $column, $code, $data, $severity, $fixable);
+
+ }//end addError()
+
+
+ /**
+ * Records a warning against a specific token in the file.
+ *
+ * @param string $warning The error message.
+ * @param int $stackPtr The stack position where the error occurred.
+ * @param string $code A violation code unique to the sniff message.
+ * @param array $data Replacements for the warning message.
+ * @param int $severity The severity level for this warning. A value of 0
+ * will be converted into the default severity level.
+ * @param boolean $fixable Can the warning be fixed by the sniff?
+ *
+ * @return boolean
+ */
+ public function addWarning(
+ $warning,
+ $stackPtr,
+ $code,
+ $data=array(),
+ $severity=0,
+ $fixable=false
+ ) {
+ if ($stackPtr === null) {
+ $line = 1;
+ $column = 1;
+ } else {
+ $line = $this->tokens[$stackPtr]['line'];
+ $column = $this->tokens[$stackPtr]['column'];
+ }
+
+ return $this->addMessage(false, $warning, $line, $column, $code, $data, $severity, $fixable);
+
+ }//end addWarning()
+
+
+ /**
+ * Records an error against a specific line in the file.
+ *
+ * @param string $error The error message.
+ * @param int $line The line on which the error occurred.
+ * @param string $code A violation code unique to the sniff message.
+ * @param array $data Replacements for the error message.
+ * @param int $severity The severity level for this error. A value of 0
+ * will be converted into the default severity level.
+ *
+ * @return boolean
+ */
+ public function addErrorOnLine(
+ $error,
+ $line,
+ $code,
+ $data=array(),
+ $severity=0
+ ) {
+ return $this->addMessage(true, $error, $line, 1, $code, $data, $severity, false);
+
+ }//end addErrorOnLine()
+
+
+ /**
+ * Records a warning against a specific token in the file.
+ *
+ * @param string $warning The error message.
+ * @param int $line The line on which the warning occurred.
+ * @param string $code A violation code unique to the sniff message.
+ * @param array $data Replacements for the warning message.
+ * @param int $severity The severity level for this warning. A value of 0 will
+ * will be converted into the default severity level.
+ *
+ * @return boolean
+ */
+ public function addWarningOnLine(
+ $warning,
+ $line,
+ $code,
+ $data=array(),
+ $severity=0
+ ) {
+ return $this->addMessage(false, $warning, $line, 1, $code, $data, $severity, false);
+
+ }//end addWarningOnLine()
+
+
+ /**
+ * Records a fixable error against a specific token in the file.
+ *
+ * Returns true if the error was recorded and should be fixed.
+ *
+ * @param string $error The error message.
+ * @param int $stackPtr The stack position where the error occurred.
+ * @param string $code A violation code unique to the sniff message.
+ * @param array $data Replacements for the error message.
+ * @param int $severity The severity level for this error. A value of 0
+ * will be converted into the default severity level.
+ *
+ * @return boolean
+ */
+ public function addFixableError(
+ $error,
+ $stackPtr,
+ $code,
+ $data=array(),
+ $severity=0
+ ) {
+ $recorded = $this->addError($error, $stackPtr, $code, $data, $severity, true);
+ if ($recorded === true && $this->fixer->enabled === true) {
+ return true;
+ }
+
+ return false;
+
+ }//end addFixableError()
+
+
+ /**
+ * Records a fixable warning against a specific token in the file.
+ *
+ * Returns true if the warning was recorded and should be fixed.
+ *
+ * @param string $warning The error message.
+ * @param int $stackPtr The stack position where the error occurred.
+ * @param string $code A violation code unique to the sniff message.
+ * @param array $data Replacements for the warning message.
+ * @param int $severity The severity level for this warning. A value of 0
+ * will be converted into the default severity level.
+ *
+ * @return boolean
+ */
+ public function addFixableWarning(
+ $warning,
+ $stackPtr,
+ $code,
+ $data=array(),
+ $severity=0
+ ) {
+ $recorded = $this->addWarning($warning, $stackPtr, $code, $data, $severity, true);
+ if ($recorded === true && $this->fixer->enabled === true) {
+ return true;
+ }
+
+ return false;
+
+ }//end addFixableWarning()
+
+
+ /**
+ * Adds an error to the error stack.
+ *
+ * @param boolean $error Is this an error message?
+ * @param string $message The text of the message.
+ * @param int $line The line on which the message occurred.
+ * @param int $column The column at which the message occurred.
+ * @param string $code A violation code unique to the sniff message.
+ * @param array $data Replacements for the message.
+ * @param int $severity The severity level for this message. A value of 0
+ * will be converted into the default severity level.
+ * @param boolean $fixable Can the problem be fixed by the sniff?
+ *
+ * @return boolean
+ */
+ protected function addMessage($error, $message, $line, $column, $code, $data, $severity, $fixable)
+ {
+ if (isset($this->tokenizer->ignoredLines[$line]) === true) {
+ return false;
+ }
+
+ $includeAll = true;
+ if ($this->configCache['cache'] === false
+ || $this->configCache['recordErrors'] === false
+ ) {
+ $includeAll = false;
+ }
+
+ // Work out which sniff generated the message.
+ $parts = explode('.', $code);
+ if ($parts[0] === 'Internal') {
+ // An internal message.
+ $listenerCode = Util\Common::getSniffCode($this->activeListener);
+ $sniffCode = $code;
+ $checkCodes = array($sniffCode);
+ } else {
+ if ($parts[0] !== $code) {
+ // The full message code has been passed in.
+ $sniffCode = $code;
+ $listenerCode = substr($sniffCode, 0, strrpos($sniffCode, '.'));
+ } else {
+ $listenerCode = Util\Common::getSniffCode($this->activeListener);
+ $sniffCode = $listenerCode.'.'.$code;
+ $parts = explode('.', $sniffCode);
+ }
+
+ $checkCodes = array(
+ $sniffCode,
+ $parts[0].'.'.$parts[1].'.'.$parts[2],
+ $parts[0].'.'.$parts[1],
+ $parts[0],
+ );
+ }//end if
+
+ // Filter out any messages for sniffs that shouldn't have run
+ // due to the use of the --sniffs command line argument.
+ if ($includeAll === false
+ && ((empty($this->configCache['sniffs']) === false
+ && in_array(strtolower($listenerCode), $this->configCache['sniffs']) === false)
+ || (empty($this->configCache['exclude']) === false
+ && in_array(strtolower($listenerCode), $this->configCache['exclude']) === true))
+ ) {
+ return false;
+ }
+
+ // If we know this sniff code is being ignored for this file, return early.
+ foreach ($checkCodes as $checkCode) {
+ if (isset($this->ignoredCodes[$checkCode]) === true) {
+ return false;
+ }
+ }
+
+ $oppositeType = 'warning';
+ if ($error === false) {
+ $oppositeType = 'error';
+ }
+
+ foreach ($checkCodes as $checkCode) {
+ // Make sure this message type has not been set to the opposite message type.
+ if (isset($this->ruleset->ruleset[$checkCode]['type']) === true
+ && $this->ruleset->ruleset[$checkCode]['type'] === $oppositeType
+ ) {
+ $error = !$error;
+ break;
+ }
+ }
+
+ if ($error === true) {
+ $configSeverity = $this->configCache['errorSeverity'];
+ $messageCount = &$this->errorCount;
+ $messages = &$this->errors;
+ } else {
+ $configSeverity = $this->configCache['warningSeverity'];
+ $messageCount = &$this->warningCount;
+ $messages = &$this->warnings;
+ }
+
+ if ($includeAll === false && $configSeverity === 0) {
+ // Don't bother doing any processing as these messages are just going to
+ // be hidden in the reports anyway.
+ return false;
+ }
+
+ if ($severity === 0) {
+ $severity = 5;
+ }
+
+ foreach ($checkCodes as $checkCode) {
+ // Make sure we are interested in this severity level.
+ if (isset($this->ruleset->ruleset[$checkCode]['severity']) === true) {
+ $severity = $this->ruleset->ruleset[$checkCode]['severity'];
+ break;
+ }
+ }
+
+ if ($includeAll === false && $configSeverity > $severity) {
+ return false;
+ }
+
+ // Make sure we are not ignoring this file.
+ foreach ($checkCodes as $checkCode) {
+ if (isset($this->configCache['ignorePatterns'][$checkCode]) === false) {
+ continue;
+ }
+
+ foreach ($this->configCache['ignorePatterns'][$checkCode] as $pattern => $type) {
+ // While there is support for a type of each pattern
+ // (absolute or relative) we don't actually support it here.
+ $replacements = array(
+ '\\,' => ',',
+ '*' => '.*',
+ );
+
+ // 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).'`i';
+ if (preg_match($pattern, $this->path) === 1) {
+ $this->ignoredCodes[$checkCode] = true;
+ return false;
+ }
+ }//end foreach
+ }//end foreach
+
+ $messageCount++;
+ if ($fixable === true) {
+ $this->fixableCount++;
+ }
+
+ if ($this->configCache['recordErrors'] === false
+ && $includeAll === false
+ ) {
+ return true;
+ }
+
+ // Work out the error message.
+ if (isset($this->ruleset->ruleset[$sniffCode]['message']) === true) {
+ $message = $this->ruleset->ruleset[$sniffCode]['message'];
+ }
+
+ if (empty($data) === false) {
+ $message = vsprintf($message, $data);
+ }
+
+ if (isset($messages[$line]) === false) {
+ $messages[$line] = array();
+ }
+
+ if (isset($messages[$line][$column]) === false) {
+ $messages[$line][$column] = array();
+ }
+
+ $messages[$line][$column][] = array(
+ 'message' => $message,
+ 'source' => $sniffCode,
+ 'listener' => $this->activeListener,
+ 'severity' => $severity,
+ 'fixable' => $fixable,
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1
+ && $this->fixer->enabled === true
+ && $fixable === true
+ ) {
+ @ob_end_clean();
+ echo "\tE: [Line $line] $message ($sniffCode)".PHP_EOL;
+ ob_start();
+ }
+
+ return true;
+
+ }//end addMessage()
+
+
+ /**
+ * Adds an warning to the warning stack.
+ *
+ * @param int $stackPtr The stack position where the metric was recorded.
+ * @param string $metric The name of the metric being recorded.
+ * @param string $value The value of the metric being recorded.
+ *
+ * @return boolean
+ */
+ public function recordMetric($stackPtr, $metric, $value)
+ {
+ if (isset($this->metrics[$metric]) === false) {
+ $this->metrics[$metric] = array('values' => array($value => 1));
+ } else {
+ if (isset($this->metrics[$metric]['values'][$value]) === false) {
+ $this->metrics[$metric]['values'][$value] = 1;
+ } else {
+ $this->metrics[$metric]['values'][$value]++;
+ }
+ }
+
+ return true;
+
+ }//end recordMetric()
+
+
+ /**
+ * Returns the number of errors raised.
+ *
+ * @return int
+ */
+ public function getErrorCount()
+ {
+ return $this->errorCount;
+
+ }//end getErrorCount()
+
+
+ /**
+ * Returns the number of warnings raised.
+ *
+ * @return int
+ */
+ public function getWarningCount()
+ {
+ return $this->warningCount;
+
+ }//end getWarningCount()
+
+
+ /**
+ * Returns the number of successes recorded.
+ *
+ * @return int
+ */
+ public function getSuccessCount()
+ {
+ return $this->successCount;
+
+ }//end getSuccessCount()
+
+
+ /**
+ * Returns the number of fixable errors/warnings raised.
+ *
+ * @return int
+ */
+ public function getFixableCount()
+ {
+ return $this->fixableCount;
+
+ }//end getFixableCount()
+
+
+ /**
+ * Returns the number of fixed errors/warnings.
+ *
+ * @return int
+ */
+ public function getFixedCount()
+ {
+ return $this->fixedCount;
+
+ }//end getFixedCount()
+
+
+ /**
+ * Returns the list of ignored lines.
+ *
+ * @return array
+ */
+ public function getIgnoredLines()
+ {
+ return $this->tokenizer->ignoredLines;
+
+ }//end getIgnoredLines()
+
+
+ /**
+ * Returns the errors raised from processing this file.
+ *
+ * @return array
+ */
+ public function getErrors()
+ {
+ return $this->errors;
+
+ }//end getErrors()
+
+
+ /**
+ * Returns the warnings raised from processing this file.
+ *
+ * @return array
+ */
+ public function getWarnings()
+ {
+ return $this->warnings;
+
+ }//end getWarnings()
+
+
+ /**
+ * Returns the metrics found while processing this file.
+ *
+ * @return array
+ */
+ public function getMetrics()
+ {
+ return $this->metrics;
+
+ }//end getMetrics()
+
+
+ /**
+ * Returns the absolute filename of this file.
+ *
+ * @return string
+ */
+ public function getFilename()
+ {
+ return $this->path;
+
+ }//end getFilename()
+
+
+ /**
+ * Returns the declaration names for classes, interfaces, traits, and functions.
+ *
+ * @param int $stackPtr The position of the declaration token which
+ * declared the class, interface, trait, or function.
+ *
+ * @return string|null The name of the class, interface, trait, or function;
+ * or NULL if the function or class is anonymous.
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified token is not of type
+ * T_FUNCTION, T_CLASS, T_ANON_CLASS,
+ * T_CLOSURE, T_TRAIT, or T_INTERFACE.
+ */
+ public function getDeclarationName($stackPtr)
+ {
+ $tokenCode = $this->tokens[$stackPtr]['code'];
+
+ if ($tokenCode === T_ANON_CLASS || $tokenCode === T_CLOSURE) {
+ return null;
+ }
+
+ if ($tokenCode !== T_FUNCTION
+ && $tokenCode !== T_CLASS
+ && $tokenCode !== T_INTERFACE
+ && $tokenCode !== T_TRAIT
+ ) {
+ throw new RuntimeException('Token type "'.$this->tokens[$stackPtr]['type'].'" is not T_FUNCTION, T_CLASS, T_INTERFACE or T_TRAIT');
+ }
+
+ if ($tokenCode === T_FUNCTION
+ && strtolower($this->tokens[$stackPtr]['content']) !== 'function'
+ ) {
+ // This is a function declared without the "function" keyword.
+ // So this token is the function name.
+ return $this->tokens[$stackPtr]['content'];
+ }
+
+ $content = null;
+ for ($i = $stackPtr; $i < $this->numTokens; $i++) {
+ if ($this->tokens[$i]['code'] === T_STRING) {
+ $content = $this->tokens[$i]['content'];
+ break;
+ }
+ }
+
+ return $content;
+
+ }//end getDeclarationName()
+
+
+ /**
+ * Returns the method parameters for the specified function token.
+ *
+ * Each parameter is in the following format:
+ *
+ * <code>
+ * 0 => array(
+ * 'name' => '$var', // The variable name.
+ * 'token' => integer, // The stack pointer to the variable name.
+ * 'content' => string, // The full content of the variable definition.
+ * 'pass_by_reference' => boolean, // Is the variable passed by reference?
+ * 'variable_length' => boolean, // Is the param of variable length through use of `...` ?
+ * 'type_hint' => string, // The type hint for the variable.
+ * 'nullable_type' => boolean, // Is the variable using a nullable type?
+ * )
+ * </code>
+ *
+ * Parameters with default values have an additional array index of
+ * 'default' with the value of the default as a string.
+ *
+ * @param int $stackPtr The position in the stack of the function token
+ * to acquire the parameters for.
+ *
+ * @return array
+ * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the specified $stackPtr is not of
+ * type T_FUNCTION or T_CLOSURE.
+ */
+ public function getMethodParameters($stackPtr)
+ {
+ if ($this->tokens[$stackPtr]['code'] !== T_FUNCTION
+ && $this->tokens[$stackPtr]['code'] !== T_CLOSURE
+ ) {
+ throw new TokenizerException('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
+ }
+
+ $opener = $this->tokens[$stackPtr]['parenthesis_opener'];
+ $closer = $this->tokens[$stackPtr]['parenthesis_closer'];
+
+ $vars = array();
+ $currVar = null;
+ $paramStart = ($opener + 1);
+ $defaultStart = null;
+ $paramCount = 0;
+ $passByReference = false;
+ $variableLength = false;
+ $typeHint = '';
+ $nullableType = false;
+
+ for ($i = $paramStart; $i <= $closer; $i++) {
+ // Check to see if this token has a parenthesis or bracket opener. If it does
+ // it's likely to be an array which might have arguments in it. This
+ // could cause problems in our parsing below, so lets just skip to the
+ // end of it.
+ if (isset($this->tokens[$i]['parenthesis_opener']) === true) {
+ // Don't do this if it's the close parenthesis for the method.
+ if ($i !== $this->tokens[$i]['parenthesis_closer']) {
+ $i = ($this->tokens[$i]['parenthesis_closer'] + 1);
+ }
+ }
+
+ if (isset($this->tokens[$i]['bracket_opener']) === true) {
+ // Don't do this if it's the close parenthesis for the method.
+ if ($i !== $this->tokens[$i]['bracket_closer']) {
+ $i = ($this->tokens[$i]['bracket_closer'] + 1);
+ }
+ }
+
+ switch ($this->tokens[$i]['code']) {
+ case T_BITWISE_AND:
+ if ($defaultStart === null) {
+ $passByReference = true;
+ }
+ break;
+ case T_VARIABLE:
+ $currVar = $i;
+ break;
+ case T_ELLIPSIS:
+ $variableLength = true;
+ break;
+ case T_ARRAY_HINT:
+ case T_CALLABLE:
+ $typeHint .= $this->tokens[$i]['content'];
+ break;
+ case T_SELF:
+ case T_PARENT:
+ case T_STATIC:
+ // Self is valid, the others invalid, but were probably intended as type hints.
+ if (isset($defaultStart) === false) {
+ $typeHint .= $this->tokens[$i]['content'];
+ }
+ break;
+ case T_STRING:
+ // This is a string, so it may be a type hint, but it could
+ // also be a constant used as a default value.
+ $prevComma = false;
+ for ($t = $i; $t >= $opener; $t--) {
+ if ($this->tokens[$t]['code'] === T_COMMA) {
+ $prevComma = $t;
+ break;
+ }
+ }
+
+ if ($prevComma !== false) {
+ $nextEquals = false;
+ for ($t = $prevComma; $t < $i; $t++) {
+ if ($this->tokens[$t]['code'] === T_EQUAL) {
+ $nextEquals = $t;
+ break;
+ }
+ }
+
+ if ($nextEquals !== false) {
+ break;
+ }
+ }
+
+ if ($defaultStart === null) {
+ $typeHint .= $this->tokens[$i]['content'];
+ }
+ break;
+ case T_NS_SEPARATOR:
+ // Part of a type hint or default value.
+ if ($defaultStart === null) {
+ $typeHint .= $this->tokens[$i]['content'];
+ }
+ break;
+ case T_NULLABLE:
+ if ($defaultStart === null) {
+ $nullableType = true;
+ $typeHint .= $this->tokens[$i]['content'];
+ }
+ break;
+ case T_CLOSE_PARENTHESIS:
+ case T_COMMA:
+ // If it's null, then there must be no parameters for this
+ // method.
+ if ($currVar === null) {
+ continue;
+ }
+
+ $vars[$paramCount] = array();
+ $vars[$paramCount]['token'] = $currVar;
+ $vars[$paramCount]['name'] = $this->tokens[$currVar]['content'];
+ $vars[$paramCount]['content'] = trim($this->getTokensAsString($paramStart, ($i - $paramStart)));
+
+ if ($defaultStart !== null) {
+ $vars[$paramCount]['default'] = trim($this->getTokensAsString($defaultStart, ($i - $defaultStart)));
+ }
+
+ $vars[$paramCount]['pass_by_reference'] = $passByReference;
+ $vars[$paramCount]['variable_length'] = $variableLength;
+ $vars[$paramCount]['type_hint'] = $typeHint;
+ $vars[$paramCount]['nullable_type'] = $nullableType;
+
+ // Reset the vars, as we are about to process the next parameter.
+ $defaultStart = null;
+ $paramStart = ($i + 1);
+ $passByReference = false;
+ $variableLength = false;
+ $typeHint = '';
+ $nullableType = false;
+
+ $paramCount++;
+ break;
+ case T_EQUAL:
+ $defaultStart = ($i + 1);
+ break;
+ }//end switch
+ }//end for
+
+ return $vars;
+
+ }//end getMethodParameters()
+
+
+ /**
+ * Returns the visibility and implementation properties of a method.
+ *
+ * The format of the array is:
+ * <code>
+ * array(
+ * 'scope' => 'public', // public protected or protected
+ * 'scope_specified' => true, // true is scope keyword was found.
+ * 'is_abstract' => false, // true if the abstract keyword was found.
+ * 'is_final' => false, // true if the final keyword was found.
+ * 'is_static' => false, // true if the static keyword was found.
+ * );
+ * </code>
+ *
+ * @param int $stackPtr The position in the stack of the function token to
+ * acquire the properties for.
+ *
+ * @return array
+ * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the specified position is not a
+ * T_FUNCTION token.
+ */
+ public function getMethodProperties($stackPtr)
+ {
+ if ($this->tokens[$stackPtr]['code'] !== T_FUNCTION
+ && $this->tokens[$stackPtr]['code'] !== T_CLOSURE
+ ) {
+ throw new TokenizerException('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
+ }
+
+ if ($this->tokens[$stackPtr]['code'] === T_FUNCTION) {
+ $valid = array(
+ T_PUBLIC => T_PUBLIC,
+ T_PRIVATE => T_PRIVATE,
+ T_PROTECTED => T_PROTECTED,
+ T_STATIC => T_STATIC,
+ T_FINAL => T_FINAL,
+ T_ABSTRACT => T_ABSTRACT,
+ T_WHITESPACE => T_WHITESPACE,
+ T_COMMENT => T_COMMENT,
+ T_DOC_COMMENT => T_DOC_COMMENT,
+ );
+ } else {
+ $valid = array(
+ T_STATIC => T_STATIC,
+ T_WHITESPACE => T_WHITESPACE,
+ T_COMMENT => T_COMMENT,
+ T_DOC_COMMENT => T_DOC_COMMENT,
+ );
+ }
+
+ $scope = 'public';
+ $scopeSpecified = false;
+ $isAbstract = false;
+ $isFinal = false;
+ $isStatic = false;
+
+ for ($i = ($stackPtr - 1); $i > 0; $i--) {
+ if (isset($valid[$this->tokens[$i]['code']]) === false) {
+ break;
+ }
+
+ switch ($this->tokens[$i]['code']) {
+ case T_PUBLIC:
+ $scope = 'public';
+ $scopeSpecified = true;
+ break;
+ case T_PRIVATE:
+ $scope = 'private';
+ $scopeSpecified = true;
+ break;
+ case T_PROTECTED:
+ $scope = 'protected';
+ $scopeSpecified = true;
+ break;
+ case T_ABSTRACT:
+ $isAbstract = true;
+ break;
+ case T_FINAL:
+ $isFinal = true;
+ break;
+ case T_STATIC:
+ $isStatic = true;
+ break;
+ }//end switch
+ }//end for
+
+ return array(
+ 'scope' => $scope,
+ 'scope_specified' => $scopeSpecified,
+ 'is_abstract' => $isAbstract,
+ 'is_final' => $isFinal,
+ 'is_static' => $isStatic,
+ );
+
+ }//end getMethodProperties()
+
+
+ /**
+ * Returns the visibility and implementation properties of the class member
+ * variable found at the specified position in the stack.
+ *
+ * The format of the array is:
+ *
+ * <code>
+ * array(
+ * 'scope' => 'public', // public protected or protected
+ * 'is_static' => false, // true if the static keyword was found.
+ * );
+ * </code>
+ *
+ * @param int $stackPtr The position in the stack of the T_VARIABLE token to
+ * acquire the properties for.
+ *
+ * @return array
+ * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the specified position is not a
+ * T_VARIABLE token, or if the position is not
+ * a class member variable.
+ */
+ public function getMemberProperties($stackPtr)
+ {
+ if ($this->tokens[$stackPtr]['code'] !== T_VARIABLE) {
+ throw new TokenizerException('$stackPtr must be of type T_VARIABLE');
+ }
+
+ $conditions = array_keys($this->tokens[$stackPtr]['conditions']);
+ $ptr = array_pop($conditions);
+ if (isset($this->tokens[$ptr]) === false
+ || ($this->tokens[$ptr]['code'] !== T_CLASS
+ && $this->tokens[$ptr]['code'] !== T_ANON_CLASS
+ && $this->tokens[$ptr]['code'] !== T_TRAIT)
+ ) {
+ if (isset($this->tokens[$ptr]) === true
+ && $this->tokens[$ptr]['code'] === T_INTERFACE
+ ) {
+ // T_VARIABLEs in interfaces can actually be method arguments
+ // but they wont be seen as being inside the method because there
+ // are no scope openers and closers for abstract methods. If it is in
+ // parentheses, we can be pretty sure it is a method argument.
+ if (isset($this->tokens[$stackPtr]['nested_parenthesis']) === false
+ || empty($this->tokens[$stackPtr]['nested_parenthesis']) === true
+ ) {
+ $error = 'Possible parse error: interfaces may not include member vars';
+ $this->addWarning($error, $stackPtr, 'Internal.ParseError.InterfaceHasMemberVar');
+ return array();
+ }
+ } else {
+ throw new TokenizerException('$stackPtr is not a class member var');
+ }
+ }
+
+ $valid = array(
+ T_PUBLIC => T_PUBLIC,
+ T_PRIVATE => T_PRIVATE,
+ T_PROTECTED => T_PROTECTED,
+ T_STATIC => T_STATIC,
+ T_WHITESPACE => T_WHITESPACE,
+ T_COMMENT => T_COMMENT,
+ T_DOC_COMMENT => T_DOC_COMMENT,
+ T_VARIABLE => T_VARIABLE,
+ T_COMMA => T_COMMA,
+ );
+
+ $scope = 'public';
+ $scopeSpecified = false;
+ $isStatic = false;
+
+ for ($i = ($stackPtr - 1); $i > 0; $i--) {
+ if (isset($valid[$this->tokens[$i]['code']]) === false) {
+ break;
+ }
+
+ switch ($this->tokens[$i]['code']) {
+ case T_PUBLIC:
+ $scope = 'public';
+ $scopeSpecified = true;
+ break;
+ case T_PRIVATE:
+ $scope = 'private';
+ $scopeSpecified = true;
+ break;
+ case T_PROTECTED:
+ $scope = 'protected';
+ $scopeSpecified = true;
+ break;
+ case T_STATIC:
+ $isStatic = true;
+ break;
+ }
+ }//end for
+
+ return array(
+ 'scope' => $scope,
+ 'scope_specified' => $scopeSpecified,
+ 'is_static' => $isStatic,
+ );
+
+ }//end getMemberProperties()
+
+
+ /**
+ * Returns the visibility and implementation properties of a class.
+ *
+ * The format of the array is:
+ * <code>
+ * array(
+ * 'is_abstract' => false, // true if the abstract keyword was found.
+ * 'is_final' => false, // true if the final keyword was found.
+ * );
+ * </code>
+ *
+ * @param int $stackPtr The position in the stack of the T_CLASS token to
+ * acquire the properties for.
+ *
+ * @return array
+ * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the specified position is not a
+ * T_CLASS token.
+ */
+ public function getClassProperties($stackPtr)
+ {
+ if ($this->tokens[$stackPtr]['code'] !== T_CLASS) {
+ throw new TokenizerException('$stackPtr must be of type T_CLASS');
+ }
+
+ $valid = array(
+ T_FINAL => T_FINAL,
+ T_ABSTRACT => T_ABSTRACT,
+ T_WHITESPACE => T_WHITESPACE,
+ T_COMMENT => T_COMMENT,
+ T_DOC_COMMENT => T_DOC_COMMENT,
+ );
+
+ $isAbstract = false;
+ $isFinal = false;
+
+ for ($i = ($stackPtr - 1); $i > 0; $i--) {
+ if (isset($valid[$this->tokens[$i]['code']]) === false) {
+ break;
+ }
+
+ switch ($this->tokens[$i]['code']) {
+ case T_ABSTRACT:
+ $isAbstract = true;
+ break;
+
+ case T_FINAL:
+ $isFinal = true;
+ break;
+ }
+ }//end for
+
+ return array(
+ 'is_abstract' => $isAbstract,
+ 'is_final' => $isFinal,
+ );
+
+ }//end getClassProperties()
+
+
+ /**
+ * Determine if the passed token is a reference operator.
+ *
+ * Returns true if the specified token position represents a reference.
+ * Returns false if the token represents a bitwise operator.
+ *
+ * @param int $stackPtr The position of the T_BITWISE_AND token.
+ *
+ * @return boolean
+ */
+ public function isReference($stackPtr)
+ {
+ if ($this->tokens[$stackPtr]['code'] !== T_BITWISE_AND) {
+ return false;
+ }
+
+ $tokenBefore = $this->findPrevious(
+ Util\Tokens::$emptyTokens,
+ ($stackPtr - 1),
+ null,
+ true
+ );
+
+ if ($this->tokens[$tokenBefore]['code'] === T_FUNCTION) {
+ // Function returns a reference.
+ return true;
+ }
+
+ if ($this->tokens[$tokenBefore]['code'] === T_DOUBLE_ARROW) {
+ // Inside a foreach loop or array assignment, this is a reference.
+ return true;
+ }
+
+ if ($this->tokens[$tokenBefore]['code'] === T_AS) {
+ // Inside a foreach loop, this is a reference.
+ return true;
+ }
+
+ if (isset(Util\Tokens::$assignmentTokens[$this->tokens[$tokenBefore]['code']]) === true) {
+ // This is directly after an assignment. It's a reference. Even if
+ // it is part of an operation, the other tests will handle it.
+ return true;
+ }
+
+ $tokenAfter = $this->findNext(
+ Util\Tokens::$emptyTokens,
+ ($stackPtr + 1),
+ null,
+ true
+ );
+
+ if ($this->tokens[$tokenAfter]['code'] === T_NEW) {
+ return true;
+ }
+
+ if (isset($this->tokens[$stackPtr]['nested_parenthesis']) === true) {
+ $brackets = $this->tokens[$stackPtr]['nested_parenthesis'];
+ $lastBracket = array_pop($brackets);
+ if (isset($this->tokens[$lastBracket]['parenthesis_owner']) === true) {
+ $owner = $this->tokens[$this->tokens[$lastBracket]['parenthesis_owner']];
+ if ($owner['code'] === T_FUNCTION
+ || $owner['code'] === T_CLOSURE
+ ) {
+ $params = $this->getMethodParameters($this->tokens[$lastBracket]['parenthesis_owner']);
+ foreach ($params as $param) {
+ $varToken = $tokenAfter;
+ if ($param['variable_length'] === true) {
+ $varToken = $this->findNext(
+ (Util\Tokens::$emptyTokens + array(T_ELLIPSIS)),
+ ($stackPtr + 1),
+ null,
+ true
+ );
+ }
+
+ if ($param['token'] === $varToken
+ && $param['pass_by_reference'] === true
+ ) {
+ // Function parameter declared to be passed by reference.
+ return true;
+ }
+ }
+ }//end if
+ } else {
+ $prev = false;
+ for ($t = ($this->tokens[$lastBracket]['parenthesis_opener'] - 1); $t >= 0; $t--) {
+ if ($this->tokens[$t]['code'] !== T_WHITESPACE) {
+ $prev = $t;
+ break;
+ }
+ }
+
+ if ($prev !== false && $this->tokens[$prev]['code'] === T_USE) {
+ // Closure use by reference.
+ return true;
+ }
+ }//end if
+ }//end if
+
+ // Pass by reference in function calls and assign by reference in arrays.
+ if ($this->tokens[$tokenBefore]['code'] === T_OPEN_PARENTHESIS
+ || $this->tokens[$tokenBefore]['code'] === T_COMMA
+ || $this->tokens[$tokenBefore]['code'] === T_OPEN_SHORT_ARRAY
+ ) {
+ if ($this->tokens[$tokenAfter]['code'] === T_VARIABLE) {
+ return true;
+ } else {
+ $skip = Util\Tokens::$emptyTokens;
+ $skip[] = T_NS_SEPARATOR;
+ $skip[] = T_SELF;
+ $skip[] = T_PARENT;
+ $skip[] = T_STATIC;
+ $skip[] = T_STRING;
+ $skip[] = T_NAMESPACE;
+ $skip[] = T_DOUBLE_COLON;
+
+ $nextSignificantAfter = $this->findNext(
+ $skip,
+ ($stackPtr + 1),
+ null,
+ true
+ );
+ if ($this->tokens[$nextSignificantAfter]['code'] === T_VARIABLE) {
+ return true;
+ }
+ }//end if
+ }//end if
+
+ return false;
+
+ }//end isReference()
+
+
+ /**
+ * Returns the content of the tokens from the specified start position in
+ * the token stack for the specified length.
+ *
+ * @param int $start The position to start from in the token stack.
+ * @param int $length The length of tokens to traverse from the start pos.
+ *
+ * @return string The token contents.
+ */
+ public function getTokensAsString($start, $length)
+ {
+ $str = '';
+ $end = ($start + $length);
+ if ($end > $this->numTokens) {
+ $end = $this->numTokens;
+ }
+
+ for ($i = $start; $i < $end; $i++) {
+ $str .= $this->tokens[$i]['content'];
+ }
+
+ return $str;
+
+ }//end getTokensAsString()
+
+
+ /**
+ * Returns the position of the previous specified token(s).
+ *
+ * If a value is specified, the previous token of the specified type(s)
+ * containing the specified value will be returned.
+ *
+ * Returns false if no token can be found.
+ *
+ * @param int|array $types The type(s) of tokens to search for.
+ * @param int $start The position to start searching from in the
+ * token stack.
+ * @param int $end The end position to fail if no token is found.
+ * if not specified or null, end will default to
+ * the start of the token stack.
+ * @param bool $exclude If true, find the previous token that is NOT of
+ * the types specified in $types.
+ * @param string $value The value that the token(s) must be equal to.
+ * If value is omitted, tokens with any value will
+ * be returned.
+ * @param bool $local If true, tokens outside the current statement
+ * will not be checked. IE. checking will stop
+ * at the previous semi-colon found.
+ *
+ * @return int|bool
+ * @see findNext()
+ */
+ public function findPrevious(
+ $types,
+ $start,
+ $end=null,
+ $exclude=false,
+ $value=null,
+ $local=false
+ ) {
+ $types = (array) $types;
+
+ if ($end === null) {
+ $end = 0;
+ }
+
+ for ($i = $start; $i >= $end; $i--) {
+ $found = (bool) $exclude;
+ foreach ($types as $type) {
+ if ($this->tokens[$i]['code'] === $type) {
+ $found = !$exclude;
+ break;
+ }
+ }
+
+ if ($found === true) {
+ if ($value === null) {
+ return $i;
+ } else if ($this->tokens[$i]['content'] === $value) {
+ return $i;
+ }
+ }
+
+ if ($local === true) {
+ if (isset($this->tokens[$i]['scope_opener']) === true
+ && $i === $this->tokens[$i]['scope_closer']
+ ) {
+ $i = $this->tokens[$i]['scope_opener'];
+ } else if (isset($this->tokens[$i]['bracket_opener']) === true
+ && $i === $this->tokens[$i]['bracket_closer']
+ ) {
+ $i = $this->tokens[$i]['bracket_opener'];
+ } else if (isset($this->tokens[$i]['parenthesis_opener']) === true
+ && $i === $this->tokens[$i]['parenthesis_closer']
+ ) {
+ $i = $this->tokens[$i]['parenthesis_opener'];
+ } else if ($this->tokens[$i]['code'] === T_SEMICOLON) {
+ break;
+ }
+ }
+ }//end for
+
+ return false;
+
+ }//end findPrevious()
+
+
+ /**
+ * Returns the position of the next specified token(s).
+ *
+ * If a value is specified, the next token of the specified type(s)
+ * containing the specified value will be returned.
+ *
+ * Returns false if no token can be found.
+ *
+ * @param int|array $types The type(s) of tokens to search for.
+ * @param int $start The position to start searching from in the
+ * token stack.
+ * @param int $end The end position to fail if no token is found.
+ * if not specified or null, end will default to
+ * the end of the token stack.
+ * @param bool $exclude If true, find the next token that is NOT of
+ * a type specified in $types.
+ * @param string $value The value that the token(s) must be equal to.
+ * If value is omitted, tokens with any value will
+ * be returned.
+ * @param bool $local If true, tokens outside the current statement
+ * will not be checked. i.e., checking will stop
+ * at the next semi-colon found.
+ *
+ * @return int|bool
+ * @see findPrevious()
+ */
+ public function findNext(
+ $types,
+ $start,
+ $end=null,
+ $exclude=false,
+ $value=null,
+ $local=false
+ ) {
+ $types = (array) $types;
+
+ if ($end === null || $end > $this->numTokens) {
+ $end = $this->numTokens;
+ }
+
+ for ($i = $start; $i < $end; $i++) {
+ $found = (bool) $exclude;
+ foreach ($types as $type) {
+ if ($this->tokens[$i]['code'] === $type) {
+ $found = !$exclude;
+ break;
+ }
+ }
+
+ if ($found === true) {
+ if ($value === null) {
+ return $i;
+ } else if ($this->tokens[$i]['content'] === $value) {
+ return $i;
+ }
+ }
+
+ if ($local === true && $this->tokens[$i]['code'] === T_SEMICOLON) {
+ break;
+ }
+ }//end for
+
+ return false;
+
+ }//end findNext()
+
+
+ /**
+ * Returns the position of the first non-whitespace token in a statement.
+ *
+ * @param int $start The position to start searching from in the token stack.
+ * @param int|array $ignore Token types that should not be considered stop points.
+ *
+ * @return int
+ */
+ public function findStartOfStatement($start, $ignore=null)
+ {
+ $endTokens = Util\Tokens::$blockOpeners;
+
+ $endTokens[T_COLON] = true;
+ $endTokens[T_COMMA] = true;
+ $endTokens[T_DOUBLE_ARROW] = true;
+ $endTokens[T_SEMICOLON] = true;
+ $endTokens[T_OPEN_TAG] = true;
+ $endTokens[T_CLOSE_TAG] = true;
+ $endTokens[T_OPEN_SHORT_ARRAY] = true;
+
+ if ($ignore !== null) {
+ $ignore = (array) $ignore;
+ foreach ($ignore as $code) {
+ if (isset($endTokens[$code]) === true) {
+ unset($endTokens[$code]);
+ }
+ }
+ }
+
+ $lastNotEmpty = $start;
+
+ for ($i = $start; $i >= 0; $i--) {
+ if (isset($endTokens[$this->tokens[$i]['code']]) === true) {
+ // Found the end of the previous statement.
+ return $lastNotEmpty;
+ }
+
+ if (isset($this->tokens[$i]['scope_opener']) === true
+ && $i === $this->tokens[$i]['scope_closer']
+ ) {
+ // Found the end of the previous scope block.
+ return $lastNotEmpty;
+ }
+
+ // Skip nested statements.
+ if (isset($this->tokens[$i]['bracket_opener']) === true
+ && $i === $this->tokens[$i]['bracket_closer']
+ ) {
+ $i = $this->tokens[$i]['bracket_opener'];
+ } else if (isset($this->tokens[$i]['parenthesis_opener']) === true
+ && $i === $this->tokens[$i]['parenthesis_closer']
+ ) {
+ $i = $this->tokens[$i]['parenthesis_opener'];
+ }
+
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$i]['code']]) === false) {
+ $lastNotEmpty = $i;
+ }
+ }//end for
+
+ return 0;
+
+ }//end findStartOfStatement()
+
+
+ /**
+ * Returns the position of the last non-whitespace token in a statement.
+ *
+ * @param int $start The position to start searching from in the token stack.
+ * @param int|array $ignore Token types that should not be considered stop points.
+ *
+ * @return int
+ */
+ public function findEndOfStatement($start, $ignore=null)
+ {
+ $endTokens = array(
+ T_COLON => true,
+ T_COMMA => true,
+ T_DOUBLE_ARROW => true,
+ T_SEMICOLON => true,
+ T_CLOSE_PARENTHESIS => true,
+ T_CLOSE_SQUARE_BRACKET => true,
+ T_CLOSE_CURLY_BRACKET => true,
+ T_CLOSE_SHORT_ARRAY => true,
+ T_OPEN_TAG => true,
+ T_CLOSE_TAG => true,
+ );
+
+ if ($ignore !== null) {
+ $ignore = (array) $ignore;
+ foreach ($ignore as $code) {
+ if (isset($endTokens[$code]) === true) {
+ unset($endTokens[$code]);
+ }
+ }
+ }
+
+ $lastNotEmpty = $start;
+
+ for ($i = $start; $i < $this->numTokens; $i++) {
+ if ($i !== $start && isset($endTokens[$this->tokens[$i]['code']]) === true) {
+ // Found the end of the statement.
+ if ($this->tokens[$i]['code'] === T_CLOSE_PARENTHESIS
+ || $this->tokens[$i]['code'] === T_CLOSE_SQUARE_BRACKET
+ || $this->tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET
+ || $this->tokens[$i]['code'] === T_CLOSE_SHORT_ARRAY
+ || $this->tokens[$i]['code'] === T_OPEN_TAG
+ || $this->tokens[$i]['code'] === T_CLOSE_TAG
+ ) {
+ return $lastNotEmpty;
+ }
+
+ return $i;
+ }
+
+ // Skip nested statements.
+ if (isset($this->tokens[$i]['scope_closer']) === true
+ && ($i === $this->tokens[$i]['scope_opener']
+ || $i === $this->tokens[$i]['scope_condition'])
+ ) {
+ $i = $this->tokens[$i]['scope_closer'];
+ } else if (isset($this->tokens[$i]['bracket_closer']) === true
+ && $i === $this->tokens[$i]['bracket_opener']
+ ) {
+ $i = $this->tokens[$i]['bracket_closer'];
+ } else if (isset($this->tokens[$i]['parenthesis_closer']) === true
+ && $i === $this->tokens[$i]['parenthesis_opener']
+ ) {
+ $i = $this->tokens[$i]['parenthesis_closer'];
+ }
+
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$i]['code']]) === false) {
+ $lastNotEmpty = $i;
+ }
+ }//end for
+
+ return ($this->numTokens - 1);
+
+ }//end findEndOfStatement()
+
+
+ /**
+ * Returns the position of the first token on a line, matching given type.
+ *
+ * Returns false if no token can be found.
+ *
+ * @param int|array $types The type(s) of tokens to search for.
+ * @param int $start The position to start searching from in the
+ * token stack. The first token matching on
+ * this line before this token will be returned.
+ * @param bool $exclude If true, find the token that is NOT of
+ * the types specified in $types.
+ * @param string $value The value that the token must be equal to.
+ * If value is omitted, tokens with any value will
+ * be returned.
+ *
+ * @return int | bool
+ */
+ public function findFirstOnLine($types, $start, $exclude=false, $value=null)
+ {
+ if (is_array($types) === false) {
+ $types = array($types);
+ }
+
+ $foundToken = false;
+
+ for ($i = $start; $i >= 0; $i--) {
+ if ($this->tokens[$i]['line'] < $this->tokens[$start]['line']) {
+ break;
+ }
+
+ $found = $exclude;
+ foreach ($types as $type) {
+ if ($exclude === false) {
+ if ($this->tokens[$i]['code'] === $type) {
+ $found = true;
+ break;
+ }
+ } else {
+ if ($this->tokens[$i]['code'] === $type) {
+ $found = false;
+ break;
+ }
+ }
+ }
+
+ if ($found === true) {
+ if ($value === null) {
+ $foundToken = $i;
+ } else if ($this->tokens[$i]['content'] === $value) {
+ $foundToken = $i;
+ }
+ }
+ }//end for
+
+ return $foundToken;
+
+ }//end findFirstOnLine()
+
+
+ /**
+ * Determine if the passed token has a condition of one of the passed types.
+ *
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|array $types The type(s) of tokens to search for.
+ *
+ * @return boolean
+ */
+ public function hasCondition($stackPtr, $types)
+ {
+ // Check for the existence of the token.
+ if (isset($this->tokens[$stackPtr]) === false) {
+ return false;
+ }
+
+ // Make sure the token has conditions.
+ if (isset($this->tokens[$stackPtr]['conditions']) === false) {
+ return false;
+ }
+
+ $types = (array) $types;
+ $conditions = $this->tokens[$stackPtr]['conditions'];
+
+ foreach ($types as $type) {
+ if (in_array($type, $conditions) === true) {
+ // We found a token with the required type.
+ return true;
+ }
+ }
+
+ return false;
+
+ }//end hasCondition()
+
+
+ /**
+ * Return the position of the condition for the passed token.
+ *
+ * Returns FALSE if the token does not have the condition.
+ *
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int $type The type of token to search for.
+ *
+ * @return int
+ */
+ public function getCondition($stackPtr, $type)
+ {
+ // Check for the existence of the token.
+ if (isset($this->tokens[$stackPtr]) === false) {
+ return false;
+ }
+
+ // Make sure the token has conditions.
+ if (isset($this->tokens[$stackPtr]['conditions']) === false) {
+ return false;
+ }
+
+ $conditions = $this->tokens[$stackPtr]['conditions'];
+ foreach ($conditions as $token => $condition) {
+ if ($condition === $type) {
+ return $token;
+ }
+ }
+
+ return false;
+
+ }//end getCondition()
+
+
+ /**
+ * Returns the name of the class that the specified class extends.
+ * (works for classes, anonymous classes and interfaces)
+ *
+ * Returns FALSE on error or if there is no extended class name.
+ *
+ * @param int $stackPtr The stack position of the class.
+ *
+ * @return string|false
+ */
+ public function findExtendedClassName($stackPtr)
+ {
+ // Check for the existence of the token.
+ if (isset($this->tokens[$stackPtr]) === false) {
+ return false;
+ }
+
+ if ($this->tokens[$stackPtr]['code'] !== T_CLASS
+ && $this->tokens[$stackPtr]['code'] !== T_ANON_CLASS
+ && $this->tokens[$stackPtr]['code'] !== T_INTERFACE
+ ) {
+ return false;
+ }
+
+ if (isset($this->tokens[$stackPtr]['scope_closer']) === false) {
+ return false;
+ }
+
+ $classCloserIndex = $this->tokens[$stackPtr]['scope_closer'];
+ $extendsIndex = $this->findNext(T_EXTENDS, $stackPtr, $classCloserIndex);
+ if (false === $extendsIndex) {
+ return false;
+ }
+
+ $find = array(
+ T_NS_SEPARATOR,
+ T_STRING,
+ T_WHITESPACE,
+ );
+
+ $end = $this->findNext($find, ($extendsIndex + 1), $classCloserIndex, true);
+ $name = $this->getTokensAsString(($extendsIndex + 1), ($end - $extendsIndex - 1));
+ $name = trim($name);
+
+ if ($name === '') {
+ return false;
+ }
+
+ return $name;
+
+ }//end findExtendedClassName()
+
+
+ /**
+ * Returns the names of the interfaces that the specified class implements.
+ *
+ * Returns FALSE on error or if there are no implemented interface names.
+ *
+ * @param int $stackPtr The stack position of the class.
+ *
+ * @return array|false
+ */
+ public function findImplementedInterfaceNames($stackPtr)
+ {
+ // Check for the existence of the token.
+ if (isset($this->tokens[$stackPtr]) === false) {
+ return false;
+ }
+
+ if ($this->tokens[$stackPtr]['code'] !== T_CLASS
+ && $this->tokens[$stackPtr]['code'] !== T_ANON_CLASS
+ ) {
+ return false;
+ }
+
+ if (isset($this->tokens[$stackPtr]['scope_closer']) === false) {
+ return false;
+ }
+
+ $classOpenerIndex = $this->tokens[$stackPtr]['scope_opener'];
+ $implementsIndex = $this->findNext(T_IMPLEMENTS, $stackPtr, $classOpenerIndex);
+ if ($implementsIndex === false) {
+ return false;
+ }
+
+ $find = array(
+ T_NS_SEPARATOR,
+ T_STRING,
+ T_WHITESPACE,
+ T_COMMA,
+ );
+
+ $end = $this->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true);
+ $name = $this->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1));
+ $name = trim($name);
+
+ if ($name === '') {
+ return false;
+ } else {
+ $names = explode(',', $name);
+ $names = array_map('trim', $names);
+ return $names;
+ }
+
+ }//end findImplementedInterfaceNames()
+
+
+}//end class
--- /dev/null
+<?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 = array();
+
+ /**
+ * 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 = array();
+
+
+ /**
+ * 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(array($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
--- /dev/null
+<?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', array(), 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) {
+ // 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 = array(
+ '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 = array();
+ $this->warnings = array();
+ $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'],
+ array(),
+ $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'],
+ array(),
+ $error['severity'],
+ $error['fixable']
+ );
+ }
+ }
+ }
+
+ }//end replayErrors()
+
+
+}//end class
--- /dev/null
+<?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
--- /dev/null
+<?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;
+
+
+ /**
+ * 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 = Util\Common::realpath($this->current());
+ if ($filePath === false) {
+ return false;
+ }
+
+ 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;
+ }
+
+ 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;
+ 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();
+ 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 = array();
+ $this->ignoreFilePatterns = array();
+
+ $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) === '/*') {
+ $this->ignoreDirPatterns[substr($pattern, 0, -2)] = $type;
+ } else {
+ $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 = array(
+ '\\,' => ',',
+ '*' => '.*',
+ );
+
+ // 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
--- /dev/null
+<?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 array();
+
+ }//end getBlacklist()
+
+
+ /**
+ * Get a list of whitelisted file paths.
+ *
+ * @return array
+ */
+ protected function getWhitelist()
+ {
+ $modified = array();
+
+ $cmd = 'git ls-files -o -m --exclude-standard -- '.escapeshellarg($this->basedir);
+ $output = array();
+ 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
--- /dev/null
+<?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 = array();
+
+ /**
+ * 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 = array();
+
+ /**
+ * 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 = array();
+
+ /**
+ * 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 = array();
+
+ /**
+ * 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 = array();
+
+ $tokens = $phpcsFile->getTokens();
+ $this->tokens = array();
+ 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;
+ }
+
+ $stdin = false;
+ if (empty($this->currentFile->config->files) === true) {
+ $stdin = true;
+ }
+
+ $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 = array();
+ 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 = array();
+ $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 = array();
+ 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 = array();
+
+ }//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 = array();
+ }//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] = array(
+ '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
--- /dev/null
+<?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 = array();
+
+
+ /**
+ * 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;
+
+ $standardFiles = array();
+ 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
--- /dev/null
+<?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('<em>', '<em>', $content);
+ $content = str_replace('</em>', '</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', '<?php', $first);
+ $first = str_replace("\n", '</br>', $first);
+ $first = str_replace(' ', ' ', $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', '<?php', $second);
+ $second = str_replace("\n", '</br>', $second);
+ $second = str_replace(' ', ' ', $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
--- /dev/null
+<?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('<em>', '*', $content);
+ $content = str_replace('</em>', '*', $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
--- /dev/null
+<?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 = array();
+ $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 = array();
+ $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 = array();
+ $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
--- /dev/null
+<?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 = array();
+
+ /**
+ * A cache of opened temporary files.
+ *
+ * @var array
+ */
+ private $tmpFiles = array();
+
+
+ /**
+ * 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] = array(
+ '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);
+ } else {
+ $flags = FILE_APPEND;
+ file_put_contents($report['output'], $generatedReport, FILE_APPEND);
+ }//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 = array(
+ 'filename' => Common::stripBasepath($phpcsFile->getFilename(), $this->config->basepath),
+ 'errors' => $phpcsFile->getErrorCount(),
+ 'warnings' => $phpcsFile->getWarningCount(),
+ 'fixable' => $phpcsFile->getFixableCount(),
+ 'messages' => array(),
+ );
+
+ 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] = array(
+ array(
+ 'message' => $message,
+ 'source' => 'Internal.RecordErrors',
+ 'severity' => 5,
+ 'fixable' => false,
+ 'type' => 'ERROR',
+ ),
+ );
+ return $report;
+ }
+
+ $errors = array();
+
+ // Merge errors and warnings.
+ foreach ($phpcsFile->getErrors() as $line => $lineErrors) {
+ foreach ($lineErrors as $column => $colErrors) {
+ $newErrors = array();
+ foreach ($colErrors as $data) {
+ $newErrors[] = array(
+ '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 = array();
+ foreach ($colWarnings as $data) {
+ $newWarnings[] = array(
+ 'message' => $data['message'],
+ 'source' => $data['source'],
+ 'severity' => $data['severity'],
+ 'fixable' => $data['fixable'],
+ 'type' => 'WARNING',
+ );
+ }
+
+ if (isset($errors[$line]) === false) {
+ $errors[$line] = array();
+ }
+
+ 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
--- /dev/null
+<?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 = array();
+ $maxLength = 0;
+ $totalFixed = 0;
+ $failures = 0;
+
+ foreach ($lines as $line) {
+ $parts = explode('>>', $line);
+ $fileLen = strlen($parts[0]);
+ $reportFiles[$parts[0]] = array(
+ '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
--- /dev/null
+<?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
--- /dev/null
+<?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 = array();
+ $lastLine = 0;
+ $stackPtr = 0;
+ foreach ($tokens as $stackPtr => $token) {
+ if ($token['line'] !== $lastLine) {
+ if ($lastLine > 0) {
+ $lineTokens[$lastLine]['end'] = ($stackPtr - 1);
+ }
+
+ $lastLine++;
+ $lineTokens[$lastLine] = array(
+ '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, array("\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
--- /dev/null
+<?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
--- /dev/null
+<?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) {
+ echo PHP_EOL;
+ }
+
+ }//end generate()
+
+
+}//end class
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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 = array();
+ $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
--- /dev/null
+<?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 = array();
+ $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
--- /dev/null
+<?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 = array();
+ foreach ($lines as $line) {
+ $parts = explode('>>', $line);
+ $metric = $parts[0];
+ $value = $parts[1];
+ $count = $parts[2];
+ if (isset($metrics[$metric]) === false) {
+ $metrics[$metric] = array();
+ }
+
+ 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
--- /dev/null
+<?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
--- /dev/null
+<?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 = array();
+ 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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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 = array();
+
+ 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] = array(
+ '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 = array();
+ $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] = array(
+ 'count' => $count,
+ 'fixable' => $fixable,
+ 'parts' => $parts,
+ );
+ } else {
+ $sources[$source]['count'] += $count;
+ }//end if
+
+ $fileLen = strlen($parts[0]);
+ $reportFiles[$parts[0]] = array(
+ '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
--- /dev/null
+<?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 = array();
+ $maxLength = 0;
+
+ foreach ($lines as $line) {
+ $parts = explode('>>', $line);
+ $fileLen = strlen($parts[0]);
+ $reportFiles[$parts[0]] = array(
+ '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
--- /dev/null
+<?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 = array();
+ 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
--- /dev/null
+<?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 = array();
+ $praiseCache = array();
+ $sourceCache = array();
+
+ 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] = array(
+ '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] = array(
+ '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] = array(
+ '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 = array();
+ $praiseCache = array();
+ $sourceCache = array();
+
+ 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]] = array(
+ '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]] = array();
+ }
+
+ if (isset($sourceCache[$parts[1]][$parts[2]]) === false) {
+ $sourceCache[$parts[1]][$parts[2]] = array(
+ '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
--- /dev/null
+<?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);
+
+ 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();
+ 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
+ ) {
+ 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
--- /dev/null
+<?php
+/**
+ * Stores the rules used to check and fix files.
+ *
+ * A ruleset object directly maps to a ruleset XML file.
+ *
+ * @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\Util;
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+
+class Ruleset
+{
+
+ /**
+ * The name of the coding standard being used.
+ *
+ * If a top-level standard includes other standards, or sniffs
+ * from other standards, only the name of the top-level standard
+ * will be stored in here.
+ *
+ * If multiple top-level standards are being loaded into
+ * a single ruleset object, this will store a comma separated list
+ * of the top-level standard names.
+ *
+ * @var string
+ */
+ public $name = '';
+
+ /**
+ * A list of file paths for the ruleset files being used.
+ *
+ * @var string[]
+ */
+ public $paths = array();
+
+ /**
+ * A list of regular expressions used to ignore specific sniffs for files and folders.
+ *
+ * Is also used to set global exclude patterns.
+ * The key is the regular expression and the value is the type
+ * of ignore pattern (absolute or relative).
+ *
+ * @var array<string, string>
+ */
+ public $ignorePatterns = array();
+
+ /**
+ * A list of regular expressions used to include specific sniffs for files and folders.
+ *
+ * The key is the sniff code and the value is an array with
+ * the key being a regular expression and the value is the type
+ * of ignore pattern (absolute or relative).
+ *
+ * @var array<string, array<string, string>>
+ */
+ public $includePatterns = array();
+
+ /**
+ * An array of sniff objects that are being used to check files.
+ *
+ * The key is the fully qualified name of the sniff class
+ * and the value is the sniff object.
+ *
+ * @var array<string, \PHP_CodeSniffer\Sniff>
+ */
+ public $sniffs = array();
+
+ /**
+ * A mapping of sniff codes to fully qualified class names.
+ *
+ * The key is the sniff code and the value
+ * is the fully qualified name of the sniff class.
+ *
+ * @var array<string, string>
+ */
+ public $sniffCodes = array();
+
+ /**
+ * An array of token types and the sniffs that are listening for them.
+ *
+ * The key is the token name being listened for and the value
+ * is the sniff object.
+ *
+ * @var array<int, \PHP_CodeSniffer\Sniff>
+ */
+ public $tokenListeners = array();
+
+ /**
+ * An array of rules from the ruleset.xml file.
+ *
+ * It may be empty, indicating that the ruleset does not override
+ * any of the default sniff settings.
+ *
+ * @var array<string, mixed>
+ */
+ public $ruleset = array();
+
+ /**
+ * The directories that the processed rulesets are in.
+ *
+ * @var string[]
+ */
+ protected $rulesetDirs = array();
+
+ /**
+ * The config data for the run.
+ *
+ * @var \PHP_CodeSniffer\Config
+ */
+ private $config = null;
+
+
+ /**
+ * Initialise the ruleset that the run will use.
+ *
+ * @param \PHP_CodeSniffer\Config $config The config data for the run.
+ *
+ * @return void
+ */
+ public function __construct(Config $config)
+ {
+ // Ignore sniff restrictions if caching is on.
+ $restrictions = array();
+ $exclusions = array();
+ if ($config->cache === false) {
+ $restrictions = $config->sniffs;
+ $exclusions = $config->exclude;
+ }
+
+ $this->config = $config;
+ $sniffs = array();
+
+ $standardPaths = array();
+ foreach ($config->standards as $standard) {
+ $installed = Util\Standards::getInstalledStandardPath($standard);
+ if ($installed === null) {
+ $standard = Util\Common::realpath($standard);
+ if (is_dir($standard) === true
+ && is_file(Util\Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml')) === true
+ ) {
+ $standard = Util\Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml');
+ }
+ } else {
+ $standard = $installed;
+ }
+
+ $standardPaths[] = $standard;
+ }
+
+ foreach ($standardPaths as $standard) {
+ $ruleset = @simplexml_load_string(file_get_contents($standard));
+ if ($ruleset !== false) {
+ $standardName = (string) $ruleset['name'];
+ if ($this->name !== '') {
+ $this->name .= ', ';
+ }
+
+ $this->name .= $standardName;
+ $this->paths[] = $standard;
+
+ // Allow autoloading of custom files inside this standard.
+ if (isset($ruleset['namespace']) === true) {
+ $namespace = (string) $ruleset['namespace'];
+ } else {
+ $namespace = basename(dirname($standard));
+ }
+
+ Autoload::addSearchPath(dirname($standard), $namespace);
+ }
+
+ if (defined('PHP_CODESNIFFER_IN_TESTS') === true && empty($restrictions) === false) {
+ // Unit tests use one standard and one sniff at a time.
+ try {
+ $sniffs = $this->expandRulesetReference($restrictions[0], dirname($standard));
+ } catch (RuntimeException $e) {
+ // Sniff reference could not be expanded, which probably means this
+ // is an installed standard. Let the unit test system take care of
+ // setting the correct sniff for testing.
+ return;
+ }
+
+ break;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY === 1) {
+ echo "Registering sniffs in the $standardName standard... ";
+ if (count($config->standards) > 1 || PHP_CODESNIFFER_VERBOSITY > 2) {
+ echo PHP_EOL;
+ }
+ }
+
+ $sniffs = array_merge($sniffs, $this->processRuleset($standard));
+ }//end foreach
+
+ $sniffRestrictions = array();
+ foreach ($restrictions as $sniffCode) {
+ $parts = explode('.', strtolower($sniffCode));
+ $sniffName = $parts[0].'\sniffs\\'.$parts[1].'\\'.$parts[2].'sniff';
+ $sniffRestrictions[$sniffName] = true;
+ }
+
+ $sniffExclusions = array();
+ foreach ($exclusions as $sniffCode) {
+ $parts = explode('.', strtolower($sniffCode));
+ $sniffName = $parts[0].'\sniffs\\'.$parts[1].'\\'.$parts[2].'sniff';
+ $sniffExclusions[$sniffName] = true;
+ }
+
+ $this->registerSniffs($sniffs, $sniffRestrictions, $sniffExclusions);
+ $this->populateTokenListeners();
+
+ $numSniffs = count($this->sniffs);
+ if (PHP_CODESNIFFER_VERBOSITY === 1) {
+ echo "DONE ($numSniffs sniffs registered)".PHP_EOL;
+ }
+
+ if ($numSniffs === 0) {
+ throw new RuntimeException('No sniffs were registered');
+ }
+
+ }//end __construct()
+
+
+ /**
+ * Prints a report showing the sniffs contained in a standard.
+ *
+ * @return void
+ */
+ public function explain()
+ {
+ $sniffs = array_keys($this->sniffCodes);
+ sort($sniffs);
+
+ ob_start();
+
+ $lastStandard = null;
+ $lastCount = '';
+ $sniffCount = count($sniffs);
+
+ // Add a dummy entry to the end so we loop
+ // one last time and clear the output buffer.
+ $sniffs[] = '';
+
+ echo PHP_EOL."The $this->name standard contains $sniffCount sniffs".PHP_EOL;
+
+ ob_start();
+
+ foreach ($sniffs as $i => $sniff) {
+ if ($i === $sniffCount) {
+ $currentStandard = null;
+ } else {
+ $currentStandard = substr($sniff, 0, strpos($sniff, '.'));
+ if ($lastStandard === null) {
+ $lastStandard = $currentStandard;
+ }
+ }
+
+ if ($currentStandard !== $lastStandard) {
+ $sniffList = ob_get_contents();
+ ob_end_clean();
+
+ echo PHP_EOL.$lastStandard.' ('.$lastCount.' sniff';
+ if ($lastCount > 1) {
+ echo 's';
+ }
+
+ echo ')'.PHP_EOL;
+ echo str_repeat('-', (strlen($lastStandard.$lastCount) + 10));
+ echo PHP_EOL;
+ echo $sniffList;
+
+ $lastStandard = $currentStandard;
+ $lastCount = 0;
+
+ if ($currentStandard === null) {
+ break;
+ }
+
+ ob_start();
+ }//end if
+
+ echo ' '.$sniff.PHP_EOL;
+ $lastCount++;
+ }//end foreach
+
+ }//end explain()
+
+
+ /**
+ * Processes a single ruleset and returns a list of the sniffs it represents.
+ *
+ * Rules founds within the ruleset are processed immediately, but sniff classes
+ * are not registered by this method.
+ *
+ * @param string $rulesetPath The path to a ruleset XML file.
+ * @param int $depth How many nested processing steps we are in. This
+ * is only used for debug output.
+ *
+ * @return string[]
+ * @throws RuntimeException If the ruleset path is invalid.
+ */
+ public function processRuleset($rulesetPath, $depth=0)
+ {
+ $rulesetPath = Util\Common::realpath($rulesetPath);
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo 'Processing ruleset '.Util\Common::stripBasepath($rulesetPath, $this->config->basepath).PHP_EOL;
+ }
+
+ $ruleset = @simplexml_load_string(file_get_contents($rulesetPath));
+ if ($ruleset === false) {
+ throw new RuntimeException("Ruleset $rulesetPath is not valid");
+ }
+
+ $ownSniffs = array();
+ $includedSniffs = array();
+ $excludedSniffs = array();
+
+ $rulesetDir = dirname($rulesetPath);
+ $this->rulesetDirs[] = $rulesetDir;
+
+ $sniffDir = $rulesetDir.DIRECTORY_SEPARATOR.'Sniffs';
+ if (is_dir($sniffDir) === true) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\tAdding sniff files from ".Util\Common::stripBasepath($sniffDir, $this->config->basepath).' directory'.PHP_EOL;
+ }
+
+ $ownSniffs = $this->expandSniffDirectory($sniffDir, $depth);
+ }
+
+ // Included custom autoloaders.
+ foreach ($ruleset->{'autoload'} as $autoload) {
+ if ($this->shouldProcessElement($autoload) === false) {
+ continue;
+ }
+
+ $autoloadPath = (string) $autoload;
+ if (is_file($autoloadPath) === false) {
+ $autoloadPath = Util\Common::realPath(dirname($rulesetPath).DIRECTORY_SEPARATOR.$autoloadPath);
+ }
+
+ if ($autoloadPath === false) {
+ throw new RuntimeException('The specified autoload file "'.$autoload.'" does not exist');
+ }
+
+ include_once $autoloadPath;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t=> included autoloader $autoloadPath".PHP_EOL;
+ }
+ }//end foreach
+
+ // Process custom sniff config settings.
+ foreach ($ruleset->{'config'} as $config) {
+ if ($this->shouldProcessElement($config) === false) {
+ continue;
+ }
+
+ Config::setConfigData((string) $config['name'], (string) $config['value'], true);
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t=> set config value ".(string) $config['name'].': '.(string) $config['value'].PHP_EOL;
+ }
+ }
+
+ foreach ($ruleset->rule as $rule) {
+ if (isset($rule['ref']) === false
+ || $this->shouldProcessElement($rule) === false
+ ) {
+ continue;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\tProcessing rule \"".$rule['ref'].'"'.PHP_EOL;
+ }
+
+ $expandedSniffs = $this->expandRulesetReference((string) $rule['ref'], $rulesetDir, $depth);
+ $newSniffs = array_diff($expandedSniffs, $includedSniffs);
+ $includedSniffs = array_merge($includedSniffs, $expandedSniffs);
+
+ $parts = explode('.', $rule['ref']);
+ if (count($parts) === 4) {
+ $sniffCode = $parts[0].'.'.$parts[1].'.'.$parts[2];
+ if (isset($this->ruleset[$sniffCode]['severity']) === true
+ && $this->ruleset[$sniffCode]['severity'] === 0
+ ) {
+ // This sniff code has already been turned off, but now
+ // it is being explicitly included again, so turn it back on.
+ $this->ruleset[(string) $rule['ref']]['severity'] = 5;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t* disabling sniff exclusion for specific message code *".PHP_EOL;
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> severity set to 5".PHP_EOL;
+ }
+ } else if (empty($newSniffs) === false) {
+ $newSniff = $newSniffs[0];
+ if (in_array($newSniff, $ownSniffs) === false) {
+ // Including a sniff that hasn't been included higher up, but
+ // only including a single message from it. So turn off all messages in
+ // the sniff, except this one.
+ $this->ruleset[$sniffCode]['severity'] = 0;
+ $this->ruleset[(string) $rule['ref']]['severity'] = 5;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\tExcluding sniff \"".$sniffCode.'" except for "'.$parts[3].'"'.PHP_EOL;
+ }
+ }
+ }//end if
+ }//end if
+
+ if (isset($rule->exclude) === true) {
+ foreach ($rule->exclude as $exclude) {
+ if (isset($exclude['name']) === false) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t* ignoring empty exclude rule *".PHP_EOL;
+ echo "\t\t\t=> ".$exclude->asXML().PHP_EOL;
+ }
+
+ continue;
+ }
+
+ if ($this->shouldProcessElement($exclude) === false) {
+ continue;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\tExcluding rule \"".$exclude['name'].'"'.PHP_EOL;
+ }
+
+ // Check if a single code is being excluded, which is a shortcut
+ // for setting the severity of the message to 0.
+ $parts = explode('.', $exclude['name']);
+ if (count($parts) === 4) {
+ $this->ruleset[(string) $exclude['name']]['severity'] = 0;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> severity set to 0".PHP_EOL;
+ }
+ } else {
+ $excludedSniffs = array_merge(
+ $excludedSniffs,
+ $this->expandRulesetReference($exclude['name'], $rulesetDir, ($depth + 1))
+ );
+ }
+ }//end foreach
+ }//end if
+
+ $this->processRule($rule, $newSniffs, $depth);
+ }//end foreach
+
+ // Process custom command line arguments.
+ $cliArgs = array();
+ foreach ($ruleset->{'arg'} as $arg) {
+ if ($this->shouldProcessElement($arg) === false) {
+ continue;
+ }
+
+ if (isset($arg['name']) === true) {
+ $argString = '--'.(string) $arg['name'];
+ if (isset($arg['value']) === true) {
+ $argString .= '='.(string) $arg['value'];
+ }
+ } else {
+ $argString = '-'.(string) $arg['value'];
+ }
+
+ $cliArgs[] = $argString;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t=> set command line value $argString".PHP_EOL;
+ }
+ }//end foreach
+
+ // Set custom php ini values as CLI args.
+ foreach ($ruleset->{'ini'} as $arg) {
+ if ($this->shouldProcessElement($arg) === false) {
+ continue;
+ }
+
+ if (isset($arg['name']) === false) {
+ continue;
+ }
+
+ $name = (string) $arg['name'];
+ $argString = $name;
+ if (isset($arg['value']) === true) {
+ $value = (string) $arg['value'];
+ $argString .= "=$value";
+ } else {
+ $value = 'true';
+ }
+
+ $cliArgs[] = '-d';
+ $cliArgs[] = $argString;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t=> set PHP ini value $name to $value".PHP_EOL;
+ }
+ }//end foreach
+
+ if (empty($this->config->files) === true) {
+ // Process hard-coded file paths.
+ foreach ($ruleset->{'file'} as $file) {
+ $file = (string) $file;
+ $cliArgs[] = $file;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t=> added \"$file\" to the file list".PHP_EOL;
+ }
+ }
+ }
+
+ if (empty($cliArgs) === false) {
+ // Change the directory so all relative paths are worked
+ // out based on the location of the ruleset instead of
+ // the location of the user.
+ $inPhar = Util\Common::isPharFile($rulesetDir);
+ if ($inPhar === false) {
+ $currentDir = getcwd();
+ chdir($rulesetDir);
+ }
+
+ $this->config->setCommandLineValues($cliArgs);
+
+ if ($inPhar === false) {
+ chdir($currentDir);
+ }
+ }
+
+ // Process custom ignore pattern rules.
+ foreach ($ruleset->{'exclude-pattern'} as $pattern) {
+ if ($this->shouldProcessElement($pattern) === false) {
+ continue;
+ }
+
+ if (isset($pattern['type']) === false) {
+ $pattern['type'] = 'absolute';
+ }
+
+ $this->ignorePatterns[(string) $pattern] = (string) $pattern['type'];
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t=> added global ".(string) $pattern['type'].' ignore pattern: '.(string) $pattern.PHP_EOL;
+ }
+ }
+
+ $includedSniffs = array_unique(array_merge($ownSniffs, $includedSniffs));
+ $excludedSniffs = array_unique($excludedSniffs);
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $included = count($includedSniffs);
+ $excluded = count($excludedSniffs);
+ echo str_repeat("\t", $depth);
+ echo "=> Ruleset processing complete; included $included sniffs and excluded $excluded".PHP_EOL;
+ }
+
+ // Merge our own sniff list with our externally included
+ // sniff list, but filter out any excluded sniffs.
+ $files = array();
+ foreach ($includedSniffs as $sniff) {
+ if (in_array($sniff, $excludedSniffs) === true) {
+ continue;
+ } else {
+ $files[] = Util\Common::realpath($sniff);
+ }
+ }
+
+ return $files;
+
+ }//end processRuleset()
+
+
+ /**
+ * Expands a directory into a list of sniff files within.
+ *
+ * @param string $directory The path to a directory.
+ * @param int $depth How many nested processing steps we are in. This
+ * is only used for debug output.
+ *
+ * @return array
+ */
+ private function expandSniffDirectory($directory, $depth=0)
+ {
+ $sniffs = array();
+
+ $rdi = new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
+ $di = new \RecursiveIteratorIterator($rdi, 0, \RecursiveIteratorIterator::CATCH_GET_CHILD);
+
+ $dirLen = strlen($directory);
+
+ foreach ($di as $file) {
+ $filename = $file->getFilename();
+
+ // Skip hidden files.
+ if (substr($filename, 0, 1) === '.') {
+ continue;
+ }
+
+ // We are only interested in PHP and sniff files.
+ $fileParts = explode('.', $filename);
+ if (array_pop($fileParts) !== 'php') {
+ continue;
+ }
+
+ $basename = basename($filename, '.php');
+ if (substr($basename, -5) !== 'Sniff') {
+ continue;
+ }
+
+ $path = $file->getPathname();
+
+ // Skip files in hidden directories within the Sniffs directory of this
+ // standard. We use the offset with strpos() to allow hidden directories
+ // before, valid example:
+ // /home/foo/.composer/vendor/squiz/custom_tool/MyStandard/Sniffs/...
+ if (strpos($path, DIRECTORY_SEPARATOR.'.', $dirLen) !== false) {
+ continue;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> ".Util\Common::stripBasepath($path, $this->config->basepath).PHP_EOL;
+ }
+
+ $sniffs[] = $path;
+ }//end foreach
+
+ return $sniffs;
+
+ }//end expandSniffDirectory()
+
+
+ /**
+ * Expands a ruleset reference into a list of sniff files.
+ *
+ * @param string $ref The reference from the ruleset XML file.
+ * @param string $rulesetDir The directory of the ruleset XML file, used to
+ * evaluate relative paths.
+ * @param int $depth How many nested processing steps we are in. This
+ * is only used for debug output.
+ *
+ * @return array
+ * @throws RuntimeException If the reference is invalid.
+ */
+ private function expandRulesetReference($ref, $rulesetDir, $depth=0)
+ {
+ // Ignore internal sniffs codes as they are used to only
+ // hide and change internal messages.
+ if (substr($ref, 0, 9) === 'Internal.') {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t* ignoring internal sniff code *".PHP_EOL;
+ }
+
+ return array();
+ }
+
+ // As sniffs can't begin with a full stop, assume references in
+ // this format are relative paths and attempt to convert them
+ // to absolute paths. If this fails, let the reference run through
+ // the normal checks and have it fail as normal.
+ if (substr($ref, 0, 1) === '.') {
+ $realpath = Util\Common::realpath($rulesetDir.'/'.$ref);
+ if ($realpath !== false) {
+ $ref = $realpath;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
+ }
+ }
+ }
+
+ // As sniffs can't begin with a tilde, assume references in
+ // this format are relative to the user's home directory.
+ if (substr($ref, 0, 2) === '~/') {
+ $realpath = Util\Common::realpath($ref);
+ if ($realpath !== false) {
+ $ref = $realpath;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
+ }
+ }
+ }
+
+ if (is_file($ref) === true) {
+ if (substr($ref, -9) === 'Sniff.php') {
+ // A single external sniff.
+ $this->rulesetDirs[] = dirname(dirname(dirname($ref)));
+ return array($ref);
+ }
+ } else {
+ // See if this is a whole standard being referenced.
+ $path = Util\Standards::getInstalledStandardPath($ref);
+ if (Util\Common::isPharFile($path) === true && strpos($path, 'ruleset.xml') === false) {
+ // If the ruleset exists inside the phar file, use it.
+ if (file_exists($path.DIRECTORY_SEPARATOR.'ruleset.xml') === true) {
+ $path = $path.DIRECTORY_SEPARATOR.'ruleset.xml';
+ } else {
+ $path = null;
+ }
+ }
+
+ if ($path !== null) {
+ $ref = $path;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
+ }
+ } else if (is_dir($ref) === false) {
+ // Work out the sniff path.
+ $sepPos = strpos($ref, DIRECTORY_SEPARATOR);
+ if ($sepPos !== false) {
+ $stdName = substr($ref, 0, $sepPos);
+ $path = substr($ref, $sepPos);
+ } else {
+ $parts = explode('.', $ref);
+ $stdName = $parts[0];
+ if (count($parts) === 1) {
+ // A whole standard?
+ $path = '';
+ } else if (count($parts) === 2) {
+ // A directory of sniffs?
+ $path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1];
+ } else {
+ // A single sniff?
+ $path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1].DIRECTORY_SEPARATOR.$parts[2].'Sniff.php';
+ }
+ }
+
+ $newRef = false;
+ $stdPath = Util\Standards::getInstalledStandardPath($stdName);
+ if ($stdPath !== null && $path !== '') {
+ if (Util\Common::isPharFile($stdPath) === true
+ && strpos($stdPath, 'ruleset.xml') === false
+ ) {
+ // Phar files can only return the directory,
+ // since ruleset can be omitted if building one standard.
+ $newRef = Util\Common::realpath($stdPath.$path);
+ } else {
+ $newRef = Util\Common::realpath(dirname($stdPath).$path);
+ }
+ }
+
+ if ($newRef === false) {
+ // The sniff is not locally installed, so check if it is being
+ // referenced as a remote sniff outside the install. We do this
+ // by looking through all directories where we have found ruleset
+ // files before, looking for ones for this particular standard,
+ // and seeing if it is in there.
+ foreach ($this->rulesetDirs as $dir) {
+ if (strtolower(basename($dir)) !== strtolower($stdName)) {
+ continue;
+ }
+
+ $newRef = Util\Common::realpath($dir.$path);
+
+ if ($newRef !== false) {
+ $ref = $newRef;
+ }
+ }
+ } else {
+ $ref = $newRef;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
+ }
+ }//end if
+ }//end if
+
+ if (is_dir($ref) === true) {
+ if (is_file($ref.DIRECTORY_SEPARATOR.'ruleset.xml') === true) {
+ // We are referencing an external coding standard.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t* rule is referencing a standard using directory name; processing *".PHP_EOL;
+ }
+
+ return $this->processRuleset($ref.DIRECTORY_SEPARATOR.'ruleset.xml', ($depth + 2));
+ } else {
+ // We are referencing a whole directory of sniffs.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t* rule is referencing a directory of sniffs *".PHP_EOL;
+ echo str_repeat("\t", $depth);
+ echo "\t\tAdding sniff files from directory".PHP_EOL;
+ }
+
+ return $this->expandSniffDirectory($ref, ($depth + 1));
+ }
+ } else {
+ if (is_file($ref) === false) {
+ $error = "Referenced sniff \"$ref\" does not exist";
+ throw new RuntimeException($error);
+ }
+
+ if (substr($ref, -9) === 'Sniff.php') {
+ // A single sniff.
+ return array($ref);
+ } else {
+ // Assume an external ruleset.xml file.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t* rule is referencing a standard using ruleset path; processing *".PHP_EOL;
+ }
+
+ return $this->processRuleset($ref, ($depth + 2));
+ }
+ }//end if
+
+ }//end expandRulesetReference()
+
+
+ /**
+ * Processes a rule from a ruleset XML file, overriding built-in defaults.
+ *
+ * @param SimpleXMLElement $rule The rule object from a ruleset XML file.
+ * @param string[] $newSniffs An array of sniffs that got included by this rule.
+ * @param int $depth How many nested processing steps we are in.
+ * This is only used for debug output.
+ *
+ * @return void
+ * @throws RuntimeException If rule settings are invalid.
+ */
+ private function processRule($rule, $newSniffs, $depth=0)
+ {
+ $ref = (string) $rule['ref'];
+ $todo = array($ref);
+
+ $parts = explode('.', $ref);
+ if (count($parts) <= 2) {
+ // We are processing a standard or a category of sniffs.
+ foreach ($newSniffs as $sniffFile) {
+ $parts = explode(DIRECTORY_SEPARATOR, $sniffFile);
+ $sniffName = array_pop($parts);
+ $sniffCategory = array_pop($parts);
+ array_pop($parts);
+ $sniffStandard = array_pop($parts);
+ $todo[] = $sniffStandard.'.'.$sniffCategory.'.'.substr($sniffName, 0, -9);
+ }
+ }
+
+ foreach ($todo as $code) {
+ // Custom severity.
+ if (isset($rule->severity) === true
+ && $this->shouldProcessElement($rule->severity) === true
+ ) {
+ if (isset($this->ruleset[$code]) === false) {
+ $this->ruleset[$code] = array();
+ }
+
+ $this->ruleset[$code]['severity'] = (int) $rule->severity;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> severity set to ".(int) $rule->severity;
+ if ($code !== $ref) {
+ echo " for $code";
+ }
+
+ echo PHP_EOL;
+ }
+ }
+
+ // Custom message type.
+ if (isset($rule->type) === true
+ && $this->shouldProcessElement($rule->type) === true
+ ) {
+ if (isset($this->ruleset[$code]) === false) {
+ $this->ruleset[$code] = array();
+ }
+
+ $type = strtolower((string) $rule->type);
+ if ($type !== 'error' && $type !== 'warning') {
+ throw new RuntimeException("Message type \"$type\" is invalid; must be \"error\" or \"warning\"");
+ }
+
+ $this->ruleset[$code]['type'] = $type;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> message type set to ".(string) $rule->type;
+ if ($code !== $ref) {
+ echo " for $code";
+ }
+
+ echo PHP_EOL;
+ }
+ }//end if
+
+ // Custom message.
+ if (isset($rule->message) === true
+ && $this->shouldProcessElement($rule->message) === true
+ ) {
+ if (isset($this->ruleset[$code]) === false) {
+ $this->ruleset[$code] = array();
+ }
+
+ $this->ruleset[$code]['message'] = (string) $rule->message;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> message set to ".(string) $rule->message;
+ if ($code !== $ref) {
+ echo " for $code";
+ }
+
+ echo PHP_EOL;
+ }
+ }
+
+ // Custom properties.
+ if (isset($rule->properties) === true
+ && $this->shouldProcessElement($rule->properties) === true
+ ) {
+ foreach ($rule->properties->property as $prop) {
+ if ($this->shouldProcessElement($prop) === false) {
+ continue;
+ }
+
+ if (isset($this->ruleset[$code]) === false) {
+ $this->ruleset[$code] = array(
+ 'properties' => array(),
+ );
+ } else if (isset($this->ruleset[$code]['properties']) === false) {
+ $this->ruleset[$code]['properties'] = array();
+ }
+
+ $name = (string) $prop['name'];
+ if (isset($prop['type']) === true
+ && (string) $prop['type'] === 'array'
+ ) {
+ $value = (string) $prop['value'];
+ $values = array();
+ foreach (explode(',', $value) as $val) {
+ $v = '';
+
+ list($k,$v) = explode('=>', $val.'=>');
+ if ($v !== '') {
+ $values[$k] = $v;
+ } else {
+ $values[] = $k;
+ }
+ }
+
+ $this->ruleset[$code]['properties'][$name] = $values;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> array property \"$name\" set to \"$value\"";
+ if ($code !== $ref) {
+ echo " for $code";
+ }
+
+ echo PHP_EOL;
+ }
+ } else {
+ $this->ruleset[$code]['properties'][$name] = (string) $prop['value'];
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> property \"$name\" set to \"".(string) $prop['value'].'"';
+ if ($code !== $ref) {
+ echo " for $code";
+ }
+
+ echo PHP_EOL;
+ }
+ }//end if
+ }//end foreach
+ }//end if
+
+ // Ignore patterns.
+ foreach ($rule->{'exclude-pattern'} as $pattern) {
+ if ($this->shouldProcessElement($pattern) === false) {
+ continue;
+ }
+
+ if (isset($this->ignorePatterns[$code]) === false) {
+ $this->ignorePatterns[$code] = array();
+ }
+
+ if (isset($pattern['type']) === false) {
+ $pattern['type'] = 'absolute';
+ }
+
+ $this->ignorePatterns[$code][(string) $pattern] = (string) $pattern['type'];
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> added rule-specific ".(string) $pattern['type'].' ignore pattern';
+ if ($code !== $ref) {
+ echo " for $code";
+ }
+
+ echo ': '.(string) $pattern.PHP_EOL;
+ }
+ }//end foreach
+
+ // Include patterns.
+ foreach ($rule->{'include-pattern'} as $pattern) {
+ if ($this->shouldProcessElement($pattern) === false) {
+ continue;
+ }
+
+ if (isset($this->includePatterns[$code]) === false) {
+ $this->includePatterns[$code] = array();
+ }
+
+ if (isset($pattern['type']) === false) {
+ $pattern['type'] = 'absolute';
+ }
+
+ $this->includePatterns[$code][(string) $pattern] = (string) $pattern['type'];
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "\t\t=> added rule-specific ".(string) $pattern['type'].' include pattern';
+ if ($code !== $ref) {
+ echo " for $code";
+ }
+
+ echo ': '.(string) $pattern.PHP_EOL;
+ }
+ }//end foreach
+ }//end foreach
+
+ }//end processRule()
+
+
+ /**
+ * Determine if an element should be processed or ignored.
+ *
+ * @param SimpleXMLElement $element An object from a ruleset XML file.
+ *
+ * @return bool
+ */
+ private function shouldProcessElement($element)
+ {
+ if (isset($element['phpcbf-only']) === false
+ && isset($element['phpcs-only']) === false
+ ) {
+ // No exceptions are being made.
+ return true;
+ }
+
+ if (PHP_CODESNIFFER_CBF === true
+ && isset($element['phpcbf-only']) === true
+ && (string) $element['phpcbf-only'] === 'true'
+ ) {
+ return true;
+ }
+
+ if (PHP_CODESNIFFER_CBF === false
+ && isset($element['phpcs-only']) === true
+ && (string) $element['phpcs-only'] === 'true'
+ ) {
+ return true;
+ }
+
+ return false;
+
+ }//end shouldProcessElement()
+
+
+ /**
+ * Loads and stores sniffs objects used for sniffing files.
+ *
+ * @param array $files Paths to the sniff files to register.
+ * @param array $restrictions The sniff class names to restrict the allowed
+ * listeners to.
+ * @param array $exclusions The sniff class names to exclude from the
+ * listeners list.
+ *
+ * @return void
+ */
+ public function registerSniffs($files, $restrictions, $exclusions)
+ {
+ $listeners = array();
+
+ foreach ($files as $file) {
+ // Work out where the position of /StandardName/Sniffs/... is
+ // so we can determine what the class will be called.
+ $sniffPos = strrpos($file, DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR);
+ if ($sniffPos === false) {
+ continue;
+ }
+
+ $slashPos = strrpos(substr($file, 0, $sniffPos), DIRECTORY_SEPARATOR);
+ if ($slashPos === false) {
+ continue;
+ }
+
+ $className = Autoload::loadFile($file);
+ $compareName = Util\Common::cleanSniffClass($className);
+
+ // If they have specified a list of sniffs to restrict to, check
+ // to see if this sniff is allowed.
+ if (empty($restrictions) === false
+ && isset($restrictions[$compareName]) === false
+ ) {
+ continue;
+ }
+
+ // If they have specified a list of sniffs to exclude, check
+ // to see if this sniff is allowed.
+ if (empty($exclusions) === false
+ && isset($exclusions[$compareName]) === true
+ ) {
+ continue;
+ }
+
+ // Skip abstract classes.
+ $reflection = new \ReflectionClass($className);
+ if ($reflection->isAbstract() === true) {
+ continue;
+ }
+
+ $listeners[$className] = $className;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 2) {
+ echo "Registered $className".PHP_EOL;
+ }
+ }//end foreach
+
+ $this->sniffs = $listeners;
+
+ }//end registerSniffs()
+
+
+ /**
+ * Populates the array of PHP_CodeSniffer_Sniff's for this file.
+ *
+ * @return void
+ * @throws RuntimeException If sniff registration fails.
+ */
+ public function populateTokenListeners()
+ {
+ // Construct a list of listeners indexed by token being listened for.
+ $this->tokenListeners = array();
+
+ foreach ($this->sniffs as $sniffClass => $sniffObject) {
+ $this->sniffs[$sniffClass] = null;
+ $this->sniffs[$sniffClass] = new $sniffClass();
+
+ $sniffCode = Util\Common::getSniffCode($sniffClass);
+ $this->sniffCodes[$sniffCode] = $sniffClass;
+
+ // Set custom properties.
+ if (isset($this->ruleset[$sniffCode]['properties']) === true) {
+ foreach ($this->ruleset[$sniffCode]['properties'] as $name => $value) {
+ $this->setSniffProperty($sniffClass, $name, $value);
+ }
+ }
+
+ $tokenizers = array();
+ $vars = get_class_vars($sniffClass);
+ if (isset($vars['supportedTokenizers']) === true) {
+ foreach ($vars['supportedTokenizers'] as $tokenizer) {
+ $tokenizers[$tokenizer] = $tokenizer;
+ }
+ } else {
+ $tokenizers = array('PHP' => 'PHP');
+ }
+
+ $tokens = $this->sniffs[$sniffClass]->register();
+ if (is_array($tokens) === false) {
+ $msg = "Sniff $sniffClass register() method must return an array";
+ throw new RuntimeException($msg);
+ }
+
+ $ignorePatterns = array();
+ $patterns = $this->getIgnorePatterns($sniffCode);
+ foreach ($patterns as $pattern => $type) {
+ $replacements = array(
+ '\\,' => ',',
+ '*' => '.*',
+ );
+
+ $ignorePatterns[] = strtr($pattern, $replacements);
+ }
+
+ $includePatterns = array();
+ $patterns = $this->getIncludePatterns($sniffCode);
+ foreach ($patterns as $pattern => $type) {
+ $replacements = array(
+ '\\,' => ',',
+ '*' => '.*',
+ );
+
+ $includePatterns[] = strtr($pattern, $replacements);
+ }
+
+ foreach ($tokens as $token) {
+ if (isset($this->tokenListeners[$token]) === false) {
+ $this->tokenListeners[$token] = array();
+ }
+
+ if (isset($this->tokenListeners[$token][$sniffClass]) === false) {
+ $this->tokenListeners[$token][$sniffClass] = array(
+ 'class' => $sniffClass,
+ 'source' => $sniffCode,
+ 'tokenizers' => $tokenizers,
+ 'ignore' => $ignorePatterns,
+ 'include' => $includePatterns,
+ );
+ }
+ }
+ }//end foreach
+
+ }//end populateTokenListeners()
+
+
+ /**
+ * Set a single property for a sniff.
+ *
+ * @param string $sniffClass The class name of the sniff.
+ * @param string $name The name of the property to change.
+ * @param string $value The new value of the property.
+ *
+ * @return void
+ */
+ public function setSniffProperty($sniffClass, $name, $value)
+ {
+ // Setting a property for a sniff we are not using.
+ if (isset($this->sniffs[$sniffClass]) === false) {
+ return;
+ }
+
+ $name = trim($name);
+ if (is_string($value) === true) {
+ $value = trim($value);
+ }
+
+ // Special case for booleans.
+ if ($value === 'true') {
+ $value = true;
+ } else if ($value === 'false') {
+ $value = false;
+ }
+
+ $this->sniffs[$sniffClass]->$name = $value;
+
+ }//end setSniffProperty()
+
+
+ /**
+ * Gets the array of ignore patterns.
+ *
+ * Optionally takes a listener to get ignore patterns specified
+ * for that sniff only.
+ *
+ * @param string $listener The listener to get patterns for. If NULL, all
+ * patterns are returned.
+ *
+ * @return array
+ */
+ public function getIgnorePatterns($listener=null)
+ {
+ if ($listener === null) {
+ return $this->ignorePatterns;
+ }
+
+ if (isset($this->ignorePatterns[$listener]) === true) {
+ return $this->ignorePatterns[$listener];
+ }
+
+ return array();
+
+ }//end getIgnorePatterns()
+
+
+ /**
+ * Gets the array of include patterns.
+ *
+ * Optionally takes a listener to get include patterns specified
+ * for that sniff only.
+ *
+ * @param string $listener The listener to get patterns for. If NULL, all
+ * patterns are returned.
+ *
+ * @return array
+ */
+ public function getIncludePatterns($listener=null)
+ {
+ if ($listener === null) {
+ return $this->includePatterns;
+ }
+
+ if (isset($this->includePatterns[$listener]) === true) {
+ return $this->includePatterns[$listener];
+ }
+
+ return array();
+
+ }//end getIncludePatterns()
+
+
+}//end class
--- /dev/null
+<?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 = array($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 = array($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 = array('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 = array('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;
+
+ $numErrors = $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(array($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 = array();
+ $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[] = array(
+ '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 = array();
+ 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 = array(
+ '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 = array();
+ 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)
+ {
+ 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
--- /dev/null
+<?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 = array();
+
+ /**
+ * Tokens that this sniff wishes to process outside of the patterns.
+ *
+ * @var int[]
+ * @see registerSupplementary()
+ * @see processSupplementary()
+ */
+ private $supplementaryTokens = array();
+
+ /**
+ * Positions in the stack where errors have occurred.
+ *
+ * @var array<int, bool>
+ */
+ private $errorPos = array();
+
+
+ /**
+ * 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 = array();
+ $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 = array(
+ 'listen_pos' => $pos,
+ 'pattern' => $parsedPattern,
+ 'pattern_code' => $pattern,
+ );
+
+ if (isset($this->parsedPatterns[$tokenType]) === false) {
+ $this->parsedPatterns[$tokenType] = array();
+ }
+
+ $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 = array();
+ 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 = array();
+ $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 = array();
+
+ // 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 = array();
+ $found = '';
+
+ $ignoreTokens = array(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)
+ );
+
+ $diff = ($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 array();
+
+ }//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 = array();
+ $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 = array('type' => 'string');
+ $lastToken = ($i - $firstToken);
+ $firstToken = ($i + 3);
+ $i = ($i + 2);
+ } else if (substr($pattern, $i, 3) === 'EOL') {
+ $specialPattern = array('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 = array('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 = array();
+ foreach ($tokens as $patternInfo) {
+ $patterns[] = array(
+ 'type' => 'token',
+ 'token' => $patternInfo['code'],
+ 'value' => $patternInfo['content'],
+ );
+ }
+
+ return $patterns;
+
+ }//end createTokenPattern()
+
+
+}//end class
--- /dev/null
+<?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 = array();
+
+ /**
+ * The type of scope opener tokens that this test wishes to listen to.
+ *
+ * @var string
+ */
+ private $scopeTokens = array();
+
+ /**
+ * 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
--- /dev/null
+<?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
+{
+
+ /**
+ * The end token of the current function that we are in.
+ *
+ * @var integer
+ */
+ private $endFunction = -1;
+
+ /**
+ * TRUE if a function is currently open.
+ *
+ * @var boolean
+ */
+ private $functionOpen = false;
+
+ /**
+ * The current PHP_CodeSniffer file that we are processing.
+ *
+ * @var \PHP_CodeSniffer\Files\File
+ */
+ protected $currentFile = null;
+
+
+ /**
+ * Constructs an AbstractVariableTest.
+ */
+ public function __construct()
+ {
+ $scopes = Tokens::$ooScopeTokens;
+
+ $listen = array(
+ T_FUNCTION,
+ 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)
+ {
+ if ($this->currentFile !== $phpcsFile) {
+ $this->currentFile = $phpcsFile;
+ $this->functionOpen = false;
+ $this->endFunction = -1;
+ }
+
+ $tokens = $phpcsFile->getTokens();
+
+ if ($stackPtr > $this->endFunction) {
+ $this->functionOpen = false;
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_FUNCTION
+ && $this->functionOpen === false
+ ) {
+ $this->functionOpen = true;
+
+ $methodProps = $phpcsFile->getMethodProperties($stackPtr);
+
+ // If the function is abstract, or is in an interface,
+ // then set the end of the function to it's closing semicolon.
+ if ($methodProps['is_abstract'] === true
+ || $tokens[$currScope]['code'] === T_INTERFACE
+ ) {
+ $this->endFunction
+ = $phpcsFile->findNext(array(T_SEMICOLON), $stackPtr);
+ } else {
+ if (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ $error = 'Possible parse error: non-abstract method defined as abstract';
+ $phpcsFile->addWarning($error, $stackPtr, 'Internal.ParseError.NonAbstractDefinedAbstract');
+ return;
+ }
+
+ $this->endFunction = $tokens[$stackPtr]['scope_closer'];
+ }
+ }//end if
+
+ 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->functionOpen === true) {
+ if ($tokens[$stackPtr]['code'] === T_VARIABLE) {
+ $this->processVariable($phpcsFile, $stackPtr);
+ }
+ } else {
+ // What if we assign a member variable to another?
+ // ie. private $_count = $this->_otherCount + 1;.
+ $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
--- /dev/null
+<?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
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<documentation title="End of File Newline">
+ <standard>
+ <![CDATA[
+ Files should end with a newline character.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="No End of File Newline">
+ <standard>
+ <![CDATA[
+ Files should not end with a newline character.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<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>
--- /dev/null
+<documentation title="Line Endings">
+ <standard>
+ <![CDATA[
+ Unix-style line endings are preferred ("\n" instead of "\r\n").
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="Line Length">
+ <standard>
+ <![CDATA[
+ It is recommended to keep lines at approximately 80 characters long for better code readability.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="Lowercased Filenames">
+ <standard>
+ <![CDATA[
+ Lowercase filenames are required.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<documentation title="Multiple Statements On a Single Line">
+ <standard>
+ <![CDATA[
+ Multiple statements are not allowed on a single line.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Two statements are spread out on two separate lines.">
+ <![CDATA[
+$foo = 1;
+$bar = 2;
+ ]]>
+ </code>
+ <code title="Invalid: Two statements are combined onto one line.">
+ <![CDATA[
+$foo = 1; $bar = 2;
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Aligning Blocks of Assignments">
+ <standard>
+ <![CDATA[
+ There should be one space on either side of an equals sign used to assign a value to a variable. In the case of a block of related assignments, more space may be inserted to promote readability.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Equals signs aligned">
+ <![CDATA[
+$shortVar <em>=</em> (1 + 2);
+$veryLongVarName <em>=</em> 'string';
+$var <em>=</em> foo($bar, $baz, $quux);
+ ]]>
+ </code>
+ <code title="Not aligned; harder to read">
+ <![CDATA[
+$shortVar <em>=</em> (1 + 2);
+$veryLongVarName <em>=</em> 'string';
+$var <em>=</em> foo($bar, $baz, $quux);
+ ]]>
+ </code>
+ </code_comparison>
+ <standard>
+ <![CDATA[
+ When using plus-equals, minus-equals etc. still ensure the equals signs are aligned to one space after the longest variable name.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Equals signs aligned; only one space after longest var name">
+ <![CDATA[
+$shortVar <em>+= </em>1;
+$veryLongVarName<em> = </em>1;
+ ]]>
+ </code>
+ <code title="Two spaces after longest var name">
+ <![CDATA[
+$shortVar <em> += </em>1;
+$veryLongVarName<em> = </em>1;
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Equals signs aligned">
+ <![CDATA[
+$shortVar <em> = </em>1;
+$veryLongVarName<em> -= </em>1;
+ ]]>
+ </code>
+ <code title="Equals signs not aligned">
+ <![CDATA[
+$shortVar <em> = </em>1;
+$veryLongVarName<em> -= </em>1;
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Space After Casts">
+ <standard>
+ <![CDATA[
+ Spaces are not allowed after casting operators.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A cast operator is immediately before its value.">
+ <![CDATA[
+$foo = (string)1;
+ ]]>
+ </code>
+ <code title="Invalid: A cast operator is followed by whitespace.">
+ <![CDATA[
+$foo = (string)<em> </em>1;
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Space After Casts">
+ <standard>
+ <![CDATA[
+ Exactly one space is allowed after a cast.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A cast operator is followed by one space.">
+ <![CDATA[
+$foo = (string)<em> </em>1;
+ ]]>
+ </code>
+ <code title="Invalid: A cast operator is not followed by whitespace.">
+ <![CDATA[
+$foo = (string)<em></em>1;
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Call-Time Pass-By-Reference">
+ <standard>
+ <![CDATA[
+ Call-time pass-by-reference is not allowed. It should be declared in the function definition.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Pass-by-reference is specified in the function definition.">
+ <![CDATA[
+function foo(<em>&</em>$bar)
+{
+ $bar++;
+}
+
+$baz = 1;
+foo($baz);
+ ]]>
+ </code>
+ <code title="Invalid: Pass-by-reference is done in the call to a function.">
+ <![CDATA[
+function foo($bar)
+{
+ $bar++;
+}
+
+$baz = 1;
+foo(<em>&</em>$baz);
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Function Argument Spacing">
+ <standard>
+ <![CDATA[
+ Function arguments should have one space after a comma, and single spaces surrounding the equals sign for default values.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Single spaces after a comma.">
+ <![CDATA[
+function foo($bar,<em> </em>$baz)
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: No spaces after a comma.">
+ <![CDATA[
+function foo($bar,<em></em>$baz)
+{
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Single spaces around an equals sign in function declaration.">
+ <![CDATA[
+function foo($bar, $baz<em> </em>=<em> </em>true)
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: No spaces around an equals sign in function declaration.">
+ <![CDATA[
+function foo($bar, $baz<em></em>=<em></em>true)
+{
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Opening Brace in Function Declarations">
+ <standard>
+ <![CDATA[
+ Function declarations follow the "BSD/Allman style". The function brace is on the line following the function declaration and is indented to the same column as the start of the function declaration.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: brace on next line">
+ <![CDATA[
+function fooFunction($arg1, $arg2 = '')
+<em>{</em>
+ ...
+}
+ ]]>
+ </code>
+ <code title="Invalid: brace on same line">
+ <![CDATA[
+function fooFunction($arg1, $arg2 = '') <em>{</em>
+ ...
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Opening Brace in Function Declarations">
+ <standard>
+ <![CDATA[
+ Function declarations follow the "Kernighan/Ritchie style". The function brace is on the same line as the function declaration. One space is required between the closing parenthesis and the brace.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: brace on same line">
+ <![CDATA[
+function fooFunction($arg1, $arg2 = '')<em> {</em>
+ ...
+}
+ ]]>
+ </code>
+ <code title="Invalid: brace on next line">
+ <![CDATA[
+function fooFunction($arg1, $arg2 = '')
+<em>{</em>
+ ...
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Cyclomatic Complexity">
+ <standard>
+ <![CDATA[
+ Functions should not have a cyclomatic complexity greater than 20, and should try to stay below a complexity of 10.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="Nesting Level">
+ <standard>
+ <![CDATA[
+ Functions should not have a nesting level greater than 10, and should try to stay below 5.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="camelCaps Function Names">
+ <standard>
+ <![CDATA[
+ Functions should use camelCaps format for their names. Only PHP's magic methods should use a double underscore prefix.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A function in camelCaps format.">
+ <![CDATA[
+function <em>doSomething</em>()
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: A function in snake_case format.">
+ <![CDATA[
+function <em>do_something</em>()
+{
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Constructor name">
+ <standard>
+ <![CDATA[
+ Constructors should be named __construct, not after the class.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: The constructor is named __construct.">
+ <![CDATA[
+class Foo
+{
+ function <em>__construct</em>()
+ {
+ }
+}
+ ]]>
+ </code>
+ <code title="Invalid: The old style class name constructor is used.">
+ <![CDATA[
+class Foo
+{
+ function <em>Foo</em>()
+ {
+ }
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Constant Names">
+ <standard>
+ <![CDATA[
+ Constants should always be all-uppercase, with underscores to separate words.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: all uppercase">
+ <![CDATA[
+define('<em>FOO_CONSTANT</em>', 'foo');
+
+class FooClass
+{
+ const <em>FOO_CONSTANT</em> = 'foo';
+}
+ ]]>
+ </code>
+ <code title="Invalid: mixed case">
+ <![CDATA[
+define('<em>Foo_Constant</em>', 'foo');
+
+class FooClass
+{
+ const <em>foo_constant</em> = 'foo';
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Backtick Operator">
+ <standard>
+ <![CDATA[
+ Disallow the use of the backtick operator for execution of shell commands.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="Opening Tag at Start of File">
+ <standard>
+ <![CDATA[
+ The opening php tag should be the first item in the file.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A file starting with an opening php tag.">
+ <![CDATA[
+<em></em><?php
+echo 'Foo';
+ ]]>
+ </code>
+ <code title="Invalid: A file with content before the opening php tag.">
+ <![CDATA[
+<em>Beginning content</em>
+<?php
+echo 'Foo';
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Closing PHP Tags">
+ <standard>
+ <![CDATA[
+ All opening php tags should have a corresponding closing tag.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A closing tag paired with it's opening tag.">
+ <![CDATA[
+<em><?php</em>
+echo 'Foo';
+<em>?></em>
+ ]]>
+ </code>
+ <code title="Invalid: No closing tag paired with the opening tag.">
+ <![CDATA[
+<em><?php</em>
+echo 'Foo';
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Deprecated Functions">
+ <standard>
+ <![CDATA[
+ Deprecated functions should not be used.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A non-deprecated function is used.">
+ <![CDATA[
+$foo = <em>explode</em>('a', $bar);
+ ]]>
+ </code>
+ <code title="Invalid: A deprecated function is used.">
+ <![CDATA[
+$foo = <em>split</em>('a', $bar);
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Alternative PHP Code Tags">
+ <standard>
+ <![CDATA[
+ Always use <?php ?> to delimit PHP code, do not use the ASP <% %> style tags nor the <script language="php"></script> tags. This is the most portable way to include PHP code on differing operating systems and setups.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="PHP Code Tags">
+ <standard>
+ <![CDATA[
+ Always use <?php ?> to delimit PHP code, not the <? ?> shorthand. This is the most portable way to include PHP code on differing operating systems and setups.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="Forbidden Functions">
+ <standard>
+ <![CDATA[
+ The forbidden functions sizeof() and delete() should not be used.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: count() is used in place of sizeof().">
+ <![CDATA[
+$foo = <em>count</em>($bar);
+ ]]>
+ </code>
+ <code title="Invalid: sizeof() is used.">
+ <![CDATA[
+$foo = <em>sizeof</em>($bar);
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="PHP Constants">
+ <standard>
+ <![CDATA[
+ The <em>true</em>, <em>false</em> and <em>null</em> constants must always be lowercase.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: lowercase constants">
+ <![CDATA[
+if ($var === <em>false</em> || $var === <em>null</em>) {
+ $var = <em>true</em>;
+}
+ ]]>
+ </code>
+ <code title="Invalid: uppercase constants">
+ <![CDATA[
+if ($var === <em>FALSE</em> || $var === <em>NULL</em>) {
+ $var = <em>TRUE</em>;
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Lowercase Keywords">
+ <standard>
+ <![CDATA[
+ All PHP keywords should be lowercase.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Lowercase array keyword used.">
+ <![CDATA[
+$foo = <em>array</em>();
+ ]]>
+ </code>
+ <code title="Invalid: Non-lowercase array keyword used.">
+ <![CDATA[
+$foo = <em>Array</em>();
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Silenced Errors">
+ <standard>
+ <![CDATA[
+ Suppressing Errors is not allowed.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: isset() is used to verify that a variable exists before trying to use it.">
+ <![CDATA[
+if (<em>isset($foo)</em> && $foo) {
+ echo "Hello\n";
+}
+ ]]>
+ </code>
+ <code title="Invalid: Errors are suppressed.">
+ <![CDATA[
+if (<em>@</em>$foo) {
+ echo "Hello\n";
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="SAPI Usage">
+ <standard>
+ <![CDATA[
+ The PHP_SAPI constant should be used instead of php_sapi_name().
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: PHP_SAPI is used.">
+ <![CDATA[
+if (<em>PHP_SAPI</em> === 'cli') {
+ echo "Hello, CLI user.";
+}
+ ]]>
+ </code>
+ <code title="Invalid: php_sapi_name() is used.">
+ <![CDATA[
+if (<em>php_sapi_name()</em> === 'cli') {
+ echo "Hello, CLI user.";
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="PHP Constants">
+ <standard>
+ <![CDATA[
+ The <em>true</em>, <em>false</em> and <em>null</em> constants must always be uppercase.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: uppercase constants">
+ <![CDATA[
+if ($var === <em>FALSE</em> || $var === <em>NULL</em>) {
+ $var = <em>TRUE</em>;
+}
+ ]]>
+ </code>
+ <code title="Invalid: lowercase constants">
+ <![CDATA[
+if ($var === <em>false</em> || $var === <em>null</em>) {
+ $var = <em>true</em>;
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Unnecessary String Concatenation">
+ <standard>
+ <![CDATA[
+ Strings should not be concatenated together.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A string can be concatenated with an expression.">
+ <![CDATA[
+echo '5 + 2 = ' . (5 + 2);
+ ]]>
+ </code>
+ <code title="Invalid: Strings should not be concatenated together.">
+ <![CDATA[
+echo 'Hello' . ' ' . 'World';
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Subversion Properties">
+ <standard>
+ <![CDATA[
+ All php files in a subversion repository should have the svn:keywords property set to 'Author Id Revision' and the svn:eol-style property set to 'native'.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="No Space Indentation">
+ <standard>
+ <![CDATA[
+ Tabs should be used for indentation instead of spaces.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="No Tab Indentation">
+ <standard>
+ <![CDATA[
+ Spaces should be used for indentation instead of tabs.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="Scope Indentation">
+ <standard>
+ <![CDATA[
+ Indentation for control structures, classes, and functions should be 4 spaces per level.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: 4 spaces are used to indent a control structure.">
+ <![CDATA[
+if ($test) {
+<em> </em>$var = 1;
+}
+ ]]>
+ </code>
+ <code title="Invalid: 8 spaces are used to indent a control structure.">
+ <![CDATA[
+if ($test) {
+<em> </em>$var = 1;
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<?php
+/**
+ * Bans the use of the PHP long array syntax.
+ *
+ * @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\Standards\Generic\Sniffs\Arrays;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DisallowLongArraySyntaxSniff implements Sniff
+{
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_ARRAY);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'no');
+
+ $error = 'Short array syntax must be used to define arrays';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
+
+ if ($fix === true) {
+ $tokens = $phpcsFile->getTokens();
+ $opener = $tokens[$stackPtr]['parenthesis_opener'];
+ $closer = $tokens[$stackPtr]['parenthesis_closer'];
+
+ $phpcsFile->fixer->beginChangeset();
+
+ if ($opener === null) {
+ $phpcsFile->fixer->replaceToken($stackPtr, '[]');
+ } else {
+ $phpcsFile->fixer->replaceToken($stackPtr, '');
+ $phpcsFile->fixer->replaceToken($opener, '[');
+ $phpcsFile->fixer->replaceToken($closer, ']');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Bans the use of the PHP short array syntax.
+ *
+ * @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\Standards\Generic\Sniffs\Arrays;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DisallowShortArraySyntaxSniff implements Sniff
+{
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_SHORT_ARRAY);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'yes');
+
+ $error = 'Short array syntax is not allowed';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
+
+ if ($fix === true) {
+ $tokens = $phpcsFile->getTokens();
+ $opener = $tokens[$stackPtr]['bracket_opener'];
+ $closer = $tokens[$stackPtr]['bracket_closer'];
+
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken($opener, 'array(');
+ $phpcsFile->fixer->replaceToken($closer, ')');
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Reports errors if the same class or interface name is used in multiple files.
+ *
+ * @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\Standards\Generic\Sniffs\Classes;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DuplicateClassNameSniff implements Sniff
+{
+
+ /**
+ * List of classes that have been found during checking.
+ *
+ * @var array
+ */
+ protected $foundClasses = array();
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $namespace = '';
+ $findTokens = array(
+ T_CLASS,
+ T_INTERFACE,
+ T_NAMESPACE,
+ T_CLOSE_TAG,
+ );
+
+ $stackPtr = $phpcsFile->findNext($findTokens, ($stackPtr + 1));
+ while ($stackPtr !== false) {
+ if ($tokens[$stackPtr]['code'] === T_CLOSE_TAG) {
+ // We can stop here. The sniff will continue from the next open
+ // tag when PHPCS reaches that token, if there is one.
+ return;
+ }
+
+ // Keep track of what namespace we are in.
+ if ($tokens[$stackPtr]['code'] === T_NAMESPACE) {
+ $nsEnd = $phpcsFile->findNext(
+ array(
+ T_NS_SEPARATOR,
+ T_STRING,
+ T_WHITESPACE,
+ ),
+ ($stackPtr + 1),
+ null,
+ true
+ );
+
+ $namespace = trim($phpcsFile->getTokensAsString(($stackPtr + 1), ($nsEnd - $stackPtr - 1)));
+ $stackPtr = $nsEnd;
+ } else {
+ $nameToken = $phpcsFile->findNext(T_STRING, $stackPtr);
+ $name = $tokens[$nameToken]['content'];
+ if ($namespace !== '') {
+ $name = $namespace.'\\'.$name;
+ }
+
+ $compareName = strtolower($name);
+ if (isset($this->foundClasses[$compareName]) === true) {
+ $type = strtolower($tokens[$stackPtr]['content']);
+ $file = $this->foundClasses[$compareName]['file'];
+ $line = $this->foundClasses[$compareName]['line'];
+ $error = 'Duplicate %s name "%s" found; first defined in %s on line %s';
+ $data = array(
+ $type,
+ $name,
+ $file,
+ $line,
+ );
+ $phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
+ } else {
+ $this->foundClasses[$compareName] = array(
+ 'file' => $phpcsFile->getFilename(),
+ 'line' => $tokens[$stackPtr]['line'],
+ );
+ }
+ }//end if
+
+ $stackPtr = $phpcsFile->findNext($findTokens, ($stackPtr + 1));
+ }//end while
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that the opening brace of a class/interface/trait is on the same line as the class declaration.
+ *
+ * @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\Standards\Generic\Sniffs\Classes;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class OpeningBraceSameLineSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_CLASS,
+ T_INTERFACE,
+ T_TRAIT,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $scopeIdentifier = $phpcsFile->findNext(T_STRING, ($stackPtr + 1));
+ $errorData = array(strtolower($tokens[$stackPtr]['content']).' '.$tokens[$scopeIdentifier]['content']);
+
+ if (isset($tokens[$stackPtr]['scope_opener']) === false) {
+ $error = 'Possible parse error: %s missing opening or closing brace';
+ $phpcsFile->addWarning($error, $stackPtr, 'MissingBrace', $errorData);
+ return;
+ }
+
+ $openingBrace = $tokens[$stackPtr]['scope_opener'];
+
+ // Is the brace on the same line as the class/interface/trait declaration ?
+ $lastClassLineToken = $phpcsFile->findPrevious(T_WHITESPACE, ($openingBrace - 1), $stackPtr, true);
+ $lastClassLine = $tokens[$lastClassLineToken]['line'];
+ $braceLine = $tokens[$openingBrace]['line'];
+ $lineDifference = ($braceLine - $lastClassLine);
+
+ if ($lineDifference > 0) {
+ $phpcsFile->recordMetric($stackPtr, 'Class opening brace placement', 'new line');
+ $error = 'Opening brace should be on the same line as the declaration for %s';
+ $fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceOnNewLine', $errorData);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->addContent($lastClassLineToken, ' {');
+ $phpcsFile->fixer->replaceToken($openingBrace, '');
+ $phpcsFile->fixer->endChangeset();
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Class opening brace placement', 'same line');
+ }
+
+ // Is the opening brace the last thing on the line ?
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($openingBrace + 1), null, true);
+ if ($tokens[$next]['line'] === $tokens[$openingBrace]['line']) {
+ if ($next === $tokens[$stackPtr]['scope_closer']) {
+ // Ignore empty classes.
+ return;
+ }
+
+ $error = 'Opening brace must be the last content on the line';
+ $fix = $phpcsFile->addFixableError($error, $openingBrace, 'ContentAfterBrace');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewline($openingBrace);
+ }
+ }
+
+ // Only continue checking if the opening brace looks good.
+ if ($lineDifference > 0) {
+ return;
+ }
+
+ // Is there precisely one space before the opening brace ?
+ if ($tokens[($openingBrace - 1)]['code'] !== T_WHITESPACE) {
+ $length = 0;
+ } else if ($tokens[($openingBrace - 1)]['content'] === "\t") {
+ $length = '\t';
+ } else {
+ $length = strlen($tokens[($openingBrace - 1)]['content']);
+ }
+
+ if ($length !== 1) {
+ $error = 'Expected 1 space before opening brace; found %s';
+ $data = array($length);
+ $fix = $phpcsFile->addFixableError($error, $openingBrace, 'SpaceBeforeBrace', $data);
+ if ($fix === true) {
+ if ($length === 0 || $length === '\t') {
+ $phpcsFile->fixer->addContentBefore($openingBrace, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($openingBrace - 1), ' ');
+ }
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Detects variable assignments being made within conditions.
+ *
+ * This is a typical code smell and more often than not a comparison was intended.
+ *
+ * Note: this sniff does not detect variable assignments in the conditional part of ternaries!
+ *
+ * @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
+ * @copyright 2017 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class AssignmentInConditionSniff implements Sniff
+{
+
+
+ /**
+ * Assignment tokens to trigger on.
+ *
+ * Set in the register() method.
+ *
+ * @var array
+ */
+ protected $assignmentTokens = array();
+
+ /**
+ * The tokens that indicate the start of a condition.
+ *
+ * @var array
+ */
+ protected $conditionStartTokens = array();
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ $this->assignmentTokens = Tokens::$assignmentTokens;
+ unset($this->assignmentTokens[T_DOUBLE_ARROW]);
+
+ $starters = Tokens::$booleanOperators;
+ $starters[T_SEMICOLON] = T_SEMICOLON;
+ $starters[T_OPEN_PARENTHESIS] = T_OPEN_PARENTHESIS;
+
+ $this->conditionStartTokens = $starters;
+
+ return array(
+ T_IF,
+ T_ELSEIF,
+ T_FOR,
+ T_SWITCH,
+ T_CASE,
+ T_WHILE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $token = $tokens[$stackPtr];
+
+ // Find the condition opener/closer.
+ if ($token['code'] === T_FOR) {
+ if (isset($token['parenthesis_opener'], $token['parenthesis_closer']) === false) {
+ return;
+ }
+
+ $semicolon = $phpcsFile->findNext(T_SEMICOLON, ($token['parenthesis_opener'] + 1), ($token['parenthesis_closer']));
+ if ($semicolon === false) {
+ return;
+ }
+
+ $opener = $semicolon;
+
+ $semicolon = $phpcsFile->findNext(T_SEMICOLON, ($opener + 1), ($token['parenthesis_closer']));
+ if ($semicolon === false) {
+ return;
+ }
+
+ $closer = $semicolon;
+ unset($semicolon);
+ } else if ($token['code'] === T_CASE) {
+ if (isset($token['scope_opener']) === false) {
+ return;
+ }
+
+ $opener = $stackPtr;
+ $closer = $token['scope_opener'];
+ } else {
+ if (isset($token['parenthesis_opener'], $token['parenthesis_closer']) === false) {
+ return;
+ }
+
+ $opener = $token['parenthesis_opener'];
+ $closer = $token['parenthesis_closer'];
+ }//end if
+
+ $startPos = $opener;
+
+ do {
+ $hasAssignment = $phpcsFile->findNext($this->assignmentTokens, ($startPos + 1), $closer);
+ if ($hasAssignment === false) {
+ return;
+ }
+
+ // Examine whether the left side is a variable.
+ $hasVariable = false;
+ $conditionStart = $startPos;
+ $altConditionStart = $phpcsFile->findPrevious($this->conditionStartTokens, ($hasAssignment - 1), $startPos);
+ if ($altConditionStart !== false) {
+ $conditionStart = $altConditionStart;
+ }
+
+ for ($i = $hasAssignment; $i > $conditionStart; $i--) {
+ if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
+ continue;
+ }
+
+ // If this is a variable or array, we've seen all we need to see.
+ if ($tokens[$i]['code'] === T_VARIABLE || $tokens[$i]['code'] === T_CLOSE_SQUARE_BRACKET) {
+ $hasVariable = true;
+ break;
+ }
+
+ // If this is a function call or something, we are OK.
+ if ($tokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
+ break;
+ }
+ }
+
+ if ($hasVariable === true) {
+ $phpcsFile->addWarning(
+ 'Variable assignment found within a condition. Did you mean to do a comparison ?',
+ $hasAssignment,
+ 'Found'
+ );
+ }
+
+ $startPos = $hasAssignment;
+ } while ($startPos < $closer);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * This sniff class detected empty statement.
+ *
+ * This sniff implements the common algorithm for empty statement body detection.
+ * A body is considered as empty if it is completely empty or it only contains
+ * whitespace characters and/or comments.
+ *
+ * <code>
+ * stmt {
+ * // foo
+ * }
+ * stmt (conditions) {
+ * // foo
+ * }
+ * </code>
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class EmptyStatementSniff implements Sniff
+{
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(
+ T_CATCH,
+ T_DO,
+ T_ELSE,
+ T_ELSEIF,
+ T_FOR,
+ T_FOREACH,
+ T_IF,
+ T_SWITCH,
+ T_TRY,
+ T_WHILE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $token = $tokens[$stackPtr];
+
+ // Skip statements without a body.
+ if (isset($token['scope_opener']) === false) {
+ return;
+ }
+
+ $next = $phpcsFile->findNext(
+ Tokens::$emptyTokens,
+ ($token['scope_opener'] + 1),
+ ($token['scope_closer'] - 1),
+ true
+ );
+
+ if ($next !== false) {
+ return;
+ }
+
+ // Get token identifier.
+ $name = strtoupper($token['content']);
+ $error = 'Empty %s statement detected';
+ $phpcsFile->addError($error, $stackPtr, 'Detected'.$name, array($name));
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Detects for-loops that can be simplified to a while-loop.
+ *
+ * This rule is based on the PMD rule catalog. Detects for-loops that can be
+ * simplified as a while-loop.
+ *
+ * <code>
+ * class Foo
+ * {
+ * public function bar($x)
+ * {
+ * for (;true;) true; // No Init or Update part, may as well be: while (true)
+ * }
+ * }
+ * </code>
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ForLoopShouldBeWhileLoopSniff implements Sniff
+{
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_FOR);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $token = $tokens[$stackPtr];
+
+ // Skip invalid statement.
+ if (isset($token['parenthesis_opener']) === false) {
+ return;
+ }
+
+ $next = ++$token['parenthesis_opener'];
+ $end = --$token['parenthesis_closer'];
+
+ $parts = array(
+ 0,
+ 0,
+ 0,
+ );
+ $index = 0;
+
+ for (; $next <= $end; ++$next) {
+ $code = $tokens[$next]['code'];
+ if ($code === T_SEMICOLON) {
+ ++$index;
+ } else if (isset(Tokens::$emptyTokens[$code]) === false) {
+ ++$parts[$index];
+ }
+ }
+
+ if ($parts[0] === 0 && $parts[2] === 0 && $parts[1] > 0) {
+ $error = 'This FOR loop can be simplified to a WHILE loop';
+ $phpcsFile->addWarning($error, $stackPtr, 'CanSimplify');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Detects for-loops that use a function call in the test expression.
+ *
+ * This rule is based on the PMD rule catalog. Detects for-loops that use a
+ * function call in the test expression.
+ *
+ * <code>
+ * class Foo
+ * {
+ * public function bar($x)
+ * {
+ * $a = array(1, 2, 3, 4);
+ * for ($i = 0; $i < count($a); $i++) {
+ * $a[$i] *= $i;
+ * }
+ * }
+ * }
+ * </code>
+ *
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ForLoopWithTestFunctionCallSniff implements Sniff
+{
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_FOR);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $token = $tokens[$stackPtr];
+
+ // Skip invalid statement.
+ if (isset($token['parenthesis_opener']) === false) {
+ return;
+ }
+
+ $next = ++$token['parenthesis_opener'];
+ $end = --$token['parenthesis_closer'];
+
+ $position = 0;
+
+ for (; $next <= $end; ++$next) {
+ $code = $tokens[$next]['code'];
+ if ($code === T_SEMICOLON) {
+ ++$position;
+ }
+
+ if ($position < 1) {
+ continue;
+ } else if ($position > 1) {
+ break;
+ } else if ($code !== T_VARIABLE && $code !== T_STRING) {
+ continue;
+ }
+
+ // Find next non empty token, if it is a open curly brace we have a
+ // function call.
+ $index = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
+
+ if ($tokens[$index]['code'] === T_OPEN_PARENTHESIS) {
+ $error = 'Avoid function calls in a FOR loop test part';
+ $phpcsFile->addWarning($error, $stackPtr, 'NotAllowed');
+ break;
+ }
+ }//end for
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Detects incrementer jumbling in for loops.
+ *
+ * This rule is based on the PMD rule catalog. The jumbling incrementer sniff
+ * detects the usage of one and the same incrementer into an outer and an inner
+ * loop. Even it is intended this is confusing code.
+ *
+ * <code>
+ * class Foo
+ * {
+ * public function bar($x)
+ * {
+ * for ($i = 0; $i < 10; $i++)
+ * {
+ * for ($k = 0; $k < 20; $i++)
+ * {
+ * echo 'Hello';
+ * }
+ * }
+ * }
+ * }
+ * </code>
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class JumbledIncrementerSniff implements Sniff
+{
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_FOR);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $token = $tokens[$stackPtr];
+
+ // Skip for-loop without body.
+ if (isset($token['scope_opener']) === false) {
+ return;
+ }
+
+ // Find incrementors for outer loop.
+ $outer = $this->findIncrementers($tokens, $token);
+
+ // Skip if empty.
+ if (count($outer) === 0) {
+ return;
+ }
+
+ // Find nested for loops.
+ $start = ++$token['scope_opener'];
+ $end = --$token['scope_closer'];
+
+ for (; $start <= $end; ++$start) {
+ if ($tokens[$start]['code'] !== T_FOR) {
+ continue;
+ }
+
+ $inner = $this->findIncrementers($tokens, $tokens[$start]);
+ $diff = array_intersect($outer, $inner);
+
+ if (count($diff) !== 0) {
+ $error = 'Loop incrementor (%s) jumbling with inner loop';
+ $data = array(join(', ', $diff));
+ $phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
+ }
+ }
+
+ }//end process()
+
+
+ /**
+ * Get all used variables in the incrementer part of a for statement.
+ *
+ * @param array(integer=>array) $tokens Array with all code sniffer tokens.
+ * @param array(string=>mixed) $token Current for loop token
+ *
+ * @return string[] List of all found incrementer variables.
+ */
+ protected function findIncrementers(array $tokens, array $token)
+ {
+ // Skip invalid statement.
+ if (isset($token['parenthesis_opener']) === false) {
+ return array();
+ }
+
+ $start = ++$token['parenthesis_opener'];
+ $end = --$token['parenthesis_closer'];
+
+ $incrementers = array();
+ $semicolons = 0;
+ for ($next = $start; $next <= $end; ++$next) {
+ $code = $tokens[$next]['code'];
+ if ($code === T_SEMICOLON) {
+ ++$semicolons;
+ } else if ($semicolons === 2 && $code === T_VARIABLE) {
+ $incrementers[] = $tokens[$next]['content'];
+ }
+ }
+
+ return $incrementers;
+
+ }//end findIncrementers()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Detects unconditional if- and elseif-statements.
+ *
+ * This rule is based on the PMD rule catalog. The Unconditional If Statement
+ * sniff detects statement conditions that are only set to one of the constant
+ * values <b>true</b> or <b>false</b>
+ *
+ * <code>
+ * class Foo
+ * {
+ * public function close()
+ * {
+ * if (true)
+ * {
+ * // ...
+ * }
+ * }
+ * }
+ * </code>
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class UnconditionalIfStatementSniff implements Sniff
+{
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(
+ T_IF,
+ T_ELSEIF,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $token = $tokens[$stackPtr];
+
+ // Skip for-loop without body.
+ if (isset($token['parenthesis_opener']) === false) {
+ return;
+ }
+
+ $next = ++$token['parenthesis_opener'];
+ $end = --$token['parenthesis_closer'];
+
+ $goodCondition = false;
+ for (; $next <= $end; ++$next) {
+ $code = $tokens[$next]['code'];
+
+ if (isset(Tokens::$emptyTokens[$code]) === true) {
+ continue;
+ } else if ($code !== T_TRUE && $code !== T_FALSE) {
+ $goodCondition = true;
+ }
+ }
+
+ if ($goodCondition === false) {
+ $error = 'Avoid IF statements that are always true or false';
+ $phpcsFile->addWarning($error, $stackPtr, 'Found');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Detects unnecessary final modifiers inside of final classes.
+ *
+ * This rule is based on the PMD rule catalog. The Unnecessary Final Modifier
+ * sniff detects the use of the final modifier inside of a final class which
+ * is unnecessary.
+ *
+ * <code>
+ * final class Foo
+ * {
+ * public final function bar()
+ * {
+ * }
+ * }
+ * </code>
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class UnnecessaryFinalModifierSniff implements Sniff
+{
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_CLASS);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $token = $tokens[$stackPtr];
+
+ // Skip for-statements without body.
+ if (isset($token['scope_opener']) === false) {
+ return;
+ }
+
+ // Fetch previous token.
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+
+ // Skip for non final class.
+ if ($prev === false || $tokens[$prev]['code'] !== T_FINAL) {
+ return;
+ }
+
+ $next = ++$token['scope_opener'];
+ $end = --$token['scope_closer'];
+
+ for (; $next <= $end; ++$next) {
+ if ($tokens[$next]['code'] === T_FINAL) {
+ $error = 'Unnecessary FINAL modifier in FINAL class';
+ $phpcsFile->addWarning($error, $next, 'Found');
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the for unused function parameters.
+ *
+ * This sniff checks that all function parameters are used in the function body.
+ * One exception is made for empty function bodies or function bodies that only
+ * contain comments. This could be useful for the classes that implement an
+ * interface that defines multiple methods but the implementation only needs some
+ * of them.
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class UnusedFunctionParameterSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_FUNCTION,
+ T_CLOSURE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $token = $tokens[$stackPtr];
+
+ // Skip broken function declarations.
+ if (isset($token['scope_opener']) === false || isset($token['parenthesis_opener']) === false) {
+ return;
+ }
+
+ $params = array();
+ foreach ($phpcsFile->getMethodParameters($stackPtr) as $param) {
+ $params[$param['name']] = $stackPtr;
+ }
+
+ $next = ++$token['scope_opener'];
+ $end = --$token['scope_closer'];
+
+ $foundContent = false;
+ $validTokens = array(
+ T_HEREDOC => T_HEREDOC,
+ T_NOWDOC => T_NOWDOC,
+ T_END_HEREDOC => T_END_HEREDOC,
+ T_END_NOWDOC => T_END_NOWDOC,
+ T_DOUBLE_QUOTED_STRING => T_DOUBLE_QUOTED_STRING,
+ );
+ $validTokens += Tokens::$emptyTokens;
+
+ for (; $next <= $end; ++$next) {
+ $token = $tokens[$next];
+ $code = $token['code'];
+
+ // Ignorable tokens.
+ if (isset(Tokens::$emptyTokens[$code]) === true) {
+ continue;
+ }
+
+ if ($foundContent === false) {
+ // A throw statement as the first content indicates an interface method.
+ if ($code === T_THROW) {
+ return;
+ }
+
+ // A return statement as the first content indicates an interface method.
+ if ($code === T_RETURN) {
+ $tmp = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
+ if ($tmp === false) {
+ return;
+ }
+
+ // There is a return.
+ if ($tokens[$tmp]['code'] === T_SEMICOLON) {
+ return;
+ }
+
+ $tmp = $phpcsFile->findNext(Tokens::$emptyTokens, ($tmp + 1), null, true);
+ if ($tmp !== false && $tokens[$tmp]['code'] === T_SEMICOLON) {
+ // There is a return <token>.
+ return;
+ }
+ }//end if
+ }//end if
+
+ $foundContent = true;
+
+ if ($code === T_VARIABLE && isset($params[$token['content']]) === true) {
+ unset($params[$token['content']]);
+ } else if ($code === T_DOLLAR) {
+ $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($next + 1), null, true);
+ if ($tokens[$nextToken]['code'] === T_OPEN_CURLY_BRACKET) {
+ $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($nextToken + 1), null, true);
+ if ($tokens[$nextToken]['code'] === T_STRING) {
+ $varContent = '$'.$tokens[$nextToken]['content'];
+ if (isset($params[$varContent]) === true) {
+ unset($params[$varContent]);
+ }
+ }
+ }
+ } else if ($code === T_DOUBLE_QUOTED_STRING
+ || $code === T_START_HEREDOC
+ || $code === T_START_NOWDOC
+ ) {
+ // Tokenize strings that can contain variables.
+ // Make sure the string is re-joined if it occurs over multiple lines.
+ $content = $token['content'];
+ for ($i = ($next + 1); $i <= $end; $i++) {
+ if (isset($validTokens[$tokens[$i]['code']]) === true) {
+ $content .= $tokens[$i]['content'];
+ $next++;
+ } else {
+ break;
+ }
+ }
+
+ $stringTokens = token_get_all(sprintf('<?php %s;?>', $content));
+ foreach ($stringTokens as $stringPtr => $stringToken) {
+ if (is_array($stringToken) === false) {
+ continue;
+ }
+
+ $varContent = '';
+ if ($stringToken[0] === T_DOLLAR_OPEN_CURLY_BRACES) {
+ $varContent = '$'.$stringTokens[($stringPtr + 1)][1];
+ } else if ($stringToken[0] === T_VARIABLE) {
+ $varContent = $stringToken[1];
+ }
+
+ if ($varContent !== '' && isset($params[$varContent]) === true) {
+ unset($params[$varContent]);
+ }
+ }
+ }//end if
+ }//end for
+
+ if ($foundContent === true && count($params) > 0) {
+ foreach ($params as $paramName => $position) {
+ $error = 'The method parameter %s is never used';
+ $data = array($paramName);
+ $phpcsFile->addWarning($error, $position, 'Found', $data);
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Detects unnecessary overridden methods that simply call their parent.
+ *
+ * This rule is based on the PMD rule catalog. The Useless Overriding Method
+ * sniff detects the use of methods that only call their parent classes's method
+ * with the same name and arguments. These methods are not required.
+ *
+ * <code>
+ * class FooBar {
+ * public function __construct($a, $b) {
+ * parent::__construct($a, $b);
+ * }
+ * }
+ * </code>
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class UselessOverridingMethodSniff implements Sniff
+{
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_FUNCTION);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $token = $tokens[$stackPtr];
+
+ // Skip function without body.
+ if (isset($token['scope_opener']) === false) {
+ return;
+ }
+
+ // Get function name.
+ $methodName = $phpcsFile->getDeclarationName($stackPtr);
+
+ // Get all parameters from method signature.
+ $signature = array();
+ foreach ($phpcsFile->getMethodParameters($stackPtr) as $param) {
+ $signature[] = $param['name'];
+ }
+
+ $next = ++$token['scope_opener'];
+ $end = --$token['scope_closer'];
+
+ for (; $next <= $end; ++$next) {
+ $code = $tokens[$next]['code'];
+
+ if (isset(Tokens::$emptyTokens[$code]) === true) {
+ continue;
+ } else if ($code === T_RETURN) {
+ continue;
+ }
+
+ break;
+ }
+
+ // Any token except 'parent' indicates correct code.
+ if ($tokens[$next]['code'] !== T_PARENT) {
+ return;
+ }
+
+ // Find next non empty token index, should be double colon.
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
+
+ // Skip for invalid code.
+ if ($next === false || $tokens[$next]['code'] !== T_DOUBLE_COLON) {
+ return;
+ }
+
+ // Find next non empty token index, should be the function name.
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
+
+ // Skip for invalid code or other method.
+ if ($next === false || $tokens[$next]['content'] !== $methodName) {
+ return;
+ }
+
+ // Find next non empty token index, should be the open parenthesis.
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
+
+ // Skip for invalid code.
+ if ($next === false || $tokens[$next]['code'] !== T_OPEN_PARENTHESIS) {
+ return;
+ }
+
+ $parameters = array('');
+ $parenthesisCount = 1;
+ $count = count($tokens);
+ for (++$next; $next < $count; ++$next) {
+ $code = $tokens[$next]['code'];
+
+ if ($code === T_OPEN_PARENTHESIS) {
+ ++$parenthesisCount;
+ } else if ($code === T_CLOSE_PARENTHESIS) {
+ --$parenthesisCount;
+ } else if ($parenthesisCount === 1 && $code === T_COMMA) {
+ $parameters[] = '';
+ } else if (isset(Tokens::$emptyTokens[$code]) === false) {
+ $parameters[(count($parameters) - 1)] .= $tokens[$next]['content'];
+ }
+
+ if ($parenthesisCount === 0) {
+ break;
+ }
+ }//end for
+
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
+ if ($next === false || $tokens[$next]['code'] !== T_SEMICOLON) {
+ return;
+ }
+
+ // Check rest of the scope.
+ for (++$next; $next <= $end; ++$next) {
+ $code = $tokens[$next]['code'];
+ // Skip for any other content.
+ if (isset(Tokens::$emptyTokens[$code]) === false) {
+ return;
+ }
+ }
+
+ $parameters = array_map('trim', $parameters);
+ $parameters = array_filter($parameters);
+
+ if (count($parameters) === count($signature) && $parameters === $signature) {
+ $phpcsFile->addWarning('Possible useless method overriding detected', $stackPtr, 'Found');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures doc blocks follow basic formatting.
+ *
+ * @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\Standards\Generic\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DocCommentSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_DOC_COMMENT_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $commentStart = $stackPtr;
+ $commentEnd = $tokens[$stackPtr]['comment_closer'];
+
+ $empty = array(
+ T_DOC_COMMENT_WHITESPACE,
+ T_DOC_COMMENT_STAR,
+ );
+
+ $short = $phpcsFile->findNext($empty, ($stackPtr + 1), $commentEnd, true);
+ if ($short === false) {
+ // No content at all.
+ $error = 'Doc comment is empty';
+ $phpcsFile->addError($error, $stackPtr, 'Empty');
+ return;
+ }
+
+ // The first line of the comment should just be the /** code.
+ if ($tokens[$short]['line'] === $tokens[$stackPtr]['line']) {
+ $error = 'The open comment tag must be the only content on the line';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'ContentAfterOpen');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->addNewline($stackPtr);
+ $phpcsFile->fixer->addContentBefore($short, '* ');
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ // The last line of the comment should just be the */ code.
+ $prev = $phpcsFile->findPrevious($empty, ($commentEnd - 1), $stackPtr, true);
+ if ($tokens[$prev]['line'] === $tokens[$commentEnd]['line']) {
+ $error = 'The close comment tag must be the only content on the line';
+ $fix = $phpcsFile->addFixableError($error, $commentEnd, 'ContentBeforeClose');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewlineBefore($commentEnd);
+ }
+ }
+
+ // Check for additional blank lines at the end of the comment.
+ if ($tokens[$prev]['line'] < ($tokens[$commentEnd]['line'] - 1)) {
+ $error = 'Additional blank lines found at end of doc comment';
+ $fix = $phpcsFile->addFixableError($error, $commentEnd, 'SpacingAfter');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($prev + 1); $i < $commentEnd; $i++) {
+ if ($tokens[($i + 1)]['line'] === $tokens[$commentEnd]['line']) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ // Check for a comment description.
+ if ($tokens[$short]['code'] !== T_DOC_COMMENT_STRING) {
+ $error = 'Missing short description in doc comment';
+ $phpcsFile->addError($error, $stackPtr, 'MissingShort');
+ return;
+ }
+
+ // No extra newline before short description.
+ if ($tokens[$short]['line'] !== ($tokens[$stackPtr]['line'] + 1)) {
+ $error = 'Doc comment short description must be on the first line';
+ $fix = $phpcsFile->addFixableError($error, $short, 'SpacingBeforeShort');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = $stackPtr; $i < $short; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$stackPtr]['line']) {
+ continue;
+ } else if ($tokens[$i]['line'] === $tokens[$short]['line']) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ // Account for the fact that a short description might cover
+ // multiple lines.
+ $shortContent = $tokens[$short]['content'];
+ $shortEnd = $short;
+ for ($i = ($short + 1); $i < $commentEnd; $i++) {
+ if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
+ if ($tokens[$i]['line'] === ($tokens[$shortEnd]['line'] + 1)) {
+ $shortContent .= $tokens[$i]['content'];
+ $shortEnd = $i;
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (preg_match('/^\p{Ll}/u', $shortContent) === 1) {
+ $error = 'Doc comment short description must start with a capital letter';
+ $phpcsFile->addError($error, $short, 'ShortNotCapital');
+ }
+
+ $long = $phpcsFile->findNext($empty, ($shortEnd + 1), ($commentEnd - 1), true);
+ if ($long !== false && $tokens[$long]['code'] === T_DOC_COMMENT_STRING) {
+ if ($tokens[$long]['line'] !== ($tokens[$shortEnd]['line'] + 2)) {
+ $error = 'There must be exactly one blank line between descriptions in a doc comment';
+ $fix = $phpcsFile->addFixableError($error, $long, 'SpacingBetween');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($shortEnd + 1); $i < $long; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$shortEnd]['line']) {
+ continue;
+ } else if ($tokens[$i]['line'] === ($tokens[$long]['line'] - 1)) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ if (preg_match('/^\p{Ll}/u', $tokens[$long]['content']) === 1) {
+ $error = 'Doc comment long description must start with a capital letter';
+ $phpcsFile->addError($error, $long, 'LongNotCapital');
+ }
+ }//end if
+
+ if (empty($tokens[$commentStart]['comment_tags']) === true) {
+ // No tags in the comment.
+ return;
+ }
+
+ $firstTag = $tokens[$commentStart]['comment_tags'][0];
+ $prev = $phpcsFile->findPrevious($empty, ($firstTag - 1), $stackPtr, true);
+ if ($tokens[$firstTag]['line'] !== ($tokens[$prev]['line'] + 2)) {
+ $error = 'There must be exactly one blank line before the tags in a doc comment';
+ $fix = $phpcsFile->addFixableError($error, $firstTag, 'SpacingBeforeTags');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($prev + 1); $i < $firstTag; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$firstTag]['line']) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $indent = str_repeat(' ', $tokens[$stackPtr]['column']);
+ $phpcsFile->fixer->addContent($prev, $phpcsFile->eolChar.$indent.'*'.$phpcsFile->eolChar);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ // Break out the tags into groups and check alignment within each.
+ // A tag group is one where there are no blank lines between tags.
+ // The param tag group is special as it requires all @param tags to be inside.
+ $tagGroups = array();
+ $groupid = 0;
+ $paramGroupid = null;
+ foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) {
+ if ($pos > 0) {
+ $prev = $phpcsFile->findPrevious(
+ T_DOC_COMMENT_STRING,
+ ($tag - 1),
+ $tokens[$commentStart]['comment_tags'][($pos - 1)]
+ );
+
+ if ($prev === false) {
+ $prev = $tokens[$commentStart]['comment_tags'][($pos - 1)];
+ }
+
+ if ($tokens[$prev]['line'] !== ($tokens[$tag]['line'] - 1)) {
+ $groupid++;
+ }
+ }
+
+ if ($tokens[$tag]['content'] === '@param') {
+ if (($paramGroupid === null
+ && empty($tagGroups[$groupid]) === false)
+ || ($paramGroupid !== null
+ && $paramGroupid !== $groupid)
+ ) {
+ $error = 'Parameter tags must be grouped together in a doc comment';
+ $phpcsFile->addError($error, $tag, 'ParamGroup');
+ }
+
+ if ($paramGroupid === null) {
+ $paramGroupid = $groupid;
+ }
+ } else if ($groupid === $paramGroupid) {
+ $error = 'Tag cannot be grouped with parameter tags in a doc comment';
+ $phpcsFile->addError($error, $tag, 'NonParamGroup');
+ }//end if
+
+ $tagGroups[$groupid][] = $tag;
+ }//end foreach
+
+ foreach ($tagGroups as $group) {
+ $maxLength = 0;
+ $paddings = array();
+ foreach ($group as $pos => $tag) {
+ $tagLength = strlen($tokens[$tag]['content']);
+ if ($tagLength > $maxLength) {
+ $maxLength = $tagLength;
+ }
+
+ // Check for a value. No value means no padding needed.
+ $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd);
+ if ($string !== false && $tokens[$string]['line'] === $tokens[$tag]['line']) {
+ $paddings[$tag] = strlen($tokens[($tag + 1)]['content']);
+ }
+ }
+
+ // Check that there was single blank line after the tag block
+ // but account for a multi-line tag comments.
+ $lastTag = $group[$pos];
+ $next = $phpcsFile->findNext(T_DOC_COMMENT_TAG, ($lastTag + 3), $commentEnd);
+ if ($next !== false) {
+ $prev = $phpcsFile->findPrevious(array(T_DOC_COMMENT_TAG, T_DOC_COMMENT_STRING), ($next - 1), $commentStart);
+ if ($tokens[$next]['line'] !== ($tokens[$prev]['line'] + 2)) {
+ $error = 'There must be a single blank line after a tag group';
+ $fix = $phpcsFile->addFixableError($error, $lastTag, 'SpacingAfterTagGroup');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($prev + 1); $i < $next; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$next]['line']) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $indent = str_repeat(' ', $tokens[$stackPtr]['column']);
+ $phpcsFile->fixer->addContent($prev, $phpcsFile->eolChar.$indent.'*'.$phpcsFile->eolChar);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ }//end if
+
+ // Now check paddings.
+ foreach ($paddings as $tag => $padding) {
+ $required = ($maxLength - strlen($tokens[$tag]['content']) + 1);
+
+ if ($padding !== $required) {
+ $error = 'Tag value indented incorrectly; expected %s spaces but found %s';
+ $data = array(
+ $required,
+ $padding,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, ($tag + 1), 'TagValueIndent', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($tag + 1), str_repeat(' ', $required));
+ }
+ }
+ }
+ }//end foreach
+
+ // If there is a param group, it needs to be first.
+ if ($paramGroupid !== null && $paramGroupid !== 0) {
+ $error = 'Parameter tags must be defined first in a doc comment';
+ $phpcsFile->addError($error, $tagGroups[$paramGroupid][0], 'ParamNotFirst');
+ }
+
+ $foundTags = array();
+ foreach ($tokens[$stackPtr]['comment_tags'] as $pos => $tag) {
+ $tagName = $tokens[$tag]['content'];
+ if (isset($foundTags[$tagName]) === true) {
+ $lastTag = $tokens[$stackPtr]['comment_tags'][($pos - 1)];
+ if ($tokens[$lastTag]['content'] !== $tagName) {
+ $error = 'Tags must be grouped together in a doc comment';
+ $phpcsFile->addError($error, $tag, 'TagsNotGrouped');
+ }
+
+ continue;
+ }
+
+ $foundTags[$tagName] = true;
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Warns about FIXME comments.
+ *
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @author Sam Graham <php-codesniffer@illusori.co.uk>
+ * @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\Standards\Generic\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class FixmeSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return Tokens::$commentTokens;
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $content = $tokens[$stackPtr]['content'];
+ $matches = array();
+ preg_match('/(?:\A|[^\p{L}]+)fixme([^\p{L}]+(.*)|\Z)/ui', $content, $matches);
+ if (empty($matches) === false) {
+ // Clear whitespace and some common characters not required at
+ // the end of a fixme message to make the error more informative.
+ $type = 'CommentFound';
+ $fixmeMessage = trim($matches[1]);
+ $fixmeMessage = trim($fixmeMessage, '-:[](). ');
+ $error = 'Comment refers to a FIXME task';
+ $data = array($fixmeMessage);
+ if ($fixmeMessage !== '') {
+ $type = 'TaskFound';
+ $error .= ' "%s"';
+ }
+
+ $phpcsFile->addError($error, $stackPtr, $type, $data);
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Warns about TODO comments.
+ *
+ * @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\Standards\Generic\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class TodoSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return Tokens::$commentTokens;
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $content = $tokens[$stackPtr]['content'];
+ $matches = array();
+ preg_match('/(?:\A|[^\p{L}]+)todo([^\p{L}]+(.*)|\Z)/ui', $content, $matches);
+ if (empty($matches) === false) {
+ // Clear whitespace and some common characters not required at
+ // the end of a to-do message to make the warning more informative.
+ $type = 'CommentFound';
+ $todoMessage = trim($matches[1]);
+ $todoMessage = trim($todoMessage, '-:[](). ');
+ $error = 'Comment refers to a TODO task';
+ $data = array($todoMessage);
+ if ($todoMessage !== '') {
+ $type = 'TaskFound';
+ $error .= ' "%s"';
+ }
+
+ $phpcsFile->addWarning($error, $stackPtr, $type, $data);
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that inline control statements are not present.
+ *
+ * @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\Standards\Generic\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class InlineControlStructureSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+ /**
+ * If true, an error will be thrown; otherwise a warning.
+ *
+ * @var boolean
+ */
+ public $error = true;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_IF,
+ T_ELSE,
+ T_ELSEIF,
+ T_FOREACH,
+ T_WHILE,
+ T_DO,
+ T_SWITCH,
+ T_FOR,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[$stackPtr]['scope_opener']) === true) {
+ $phpcsFile->recordMetric($stackPtr, 'Control structure defined inline', 'no');
+ return;
+ }
+
+ // Ignore the ELSE in ELSE IF. We'll process the IF part later.
+ if ($tokens[$stackPtr]['code'] === T_ELSE) {
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$next]['code'] === T_IF) {
+ return;
+ }
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_WHILE) {
+ // This could be from a DO WHILE, which doesn't have an opening brace.
+ $lastContent = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$lastContent]['code'] === T_CLOSE_CURLY_BRACKET) {
+ $brace = $tokens[$lastContent];
+ if (isset($brace['scope_condition']) === true) {
+ $condition = $tokens[$brace['scope_condition']];
+ if ($condition['code'] === T_DO) {
+ return;
+ }
+ }
+ }
+
+ // In Javascript DO WHILE loops without curly braces are legal. This
+ // is only valid if a single statement is present between the DO and
+ // the WHILE. We can detect this by checking only a single semicolon
+ // is present between them.
+ if ($phpcsFile->tokenizerType === 'JS') {
+ $lastDo = $phpcsFile->findPrevious(T_DO, ($stackPtr - 1));
+ $lastSemicolon = $phpcsFile->findPrevious(T_SEMICOLON, ($stackPtr - 1));
+ if ($lastDo !== false && $lastSemicolon !== false && $lastDo < $lastSemicolon) {
+ $precedingSemicolon = $phpcsFile->findPrevious(T_SEMICOLON, ($lastSemicolon - 1));
+ if ($precedingSemicolon === false || $precedingSemicolon < $lastDo) {
+ return;
+ }
+ }
+ }
+ }//end if
+
+ // This is a control structure without an opening brace,
+ // so it is an inline statement.
+ if ($this->error === true) {
+ $fix = $phpcsFile->addFixableError('Inline control structures are not allowed', $stackPtr, 'NotAllowed');
+ } else {
+ $fix = $phpcsFile->addFixableWarning('Inline control structures are discouraged', $stackPtr, 'Discouraged');
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Control structure defined inline', 'yes');
+
+ // Stop here if we are not fixing the error.
+ if ($fix !== true) {
+ return;
+ }
+
+ $phpcsFile->fixer->beginChangeset();
+ if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
+ $closer = $tokens[$stackPtr]['parenthesis_closer'];
+ } else {
+ $closer = $stackPtr;
+ }
+
+ if ($tokens[($closer + 1)]['code'] === T_WHITESPACE
+ || $tokens[($closer + 1)]['code'] === T_SEMICOLON
+ ) {
+ $phpcsFile->fixer->addContent($closer, ' {');
+ } else {
+ $phpcsFile->fixer->addContent($closer, ' { ');
+ }
+
+ $fixableScopeOpeners = $this->register();
+
+ $lastNonEmpty = $closer;
+ for ($end = ($closer + 1); $end < $phpcsFile->numTokens; $end++) {
+ if ($tokens[$end]['code'] === T_SEMICOLON) {
+ break;
+ }
+
+ if ($tokens[$end]['code'] === T_CLOSE_TAG) {
+ $end = $lastNonEmpty;
+ break;
+ }
+
+ if (in_array($tokens[$end]['code'], $fixableScopeOpeners) === true
+ && isset($tokens[$end]['scope_opener']) === false
+ ) {
+ // The best way to fix nested inline scopes is middle-out.
+ // So skip this one. It will be detected and fixed on a future loop.
+ $phpcsFile->fixer->rollbackChangeset();
+ return;
+ }
+
+ if (isset($tokens[$end]['scope_opener']) === true) {
+ $type = $tokens[$end]['code'];
+ $end = $tokens[$end]['scope_closer'];
+ if ($type === T_DO || $type === T_IF || $type === T_ELSEIF || $type === T_TRY) {
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true);
+ if ($next === false) {
+ break;
+ }
+
+ $nextType = $tokens[$next]['code'];
+
+ // Let additional conditions loop and find their ending.
+ if (($type === T_IF
+ || $type === T_ELSEIF)
+ && ($nextType === T_ELSEIF
+ || $nextType === T_ELSE)
+ ) {
+ continue;
+ }
+
+ // Account for DO... WHILE conditions.
+ if ($type === T_DO && $nextType === T_WHILE) {
+ $end = $phpcsFile->findNext(T_SEMICOLON, ($next + 1));
+ }
+
+ // Account for TRY... CATCH statements.
+ if ($type === T_TRY && $nextType === T_CATCH) {
+ $end = $tokens[$next]['scope_closer'];
+ }
+ }//end if
+
+ if ($tokens[$end]['code'] !== T_END_HEREDOC
+ && $tokens[$end]['code'] !== T_END_NOWDOC
+ ) {
+ break;
+ }
+ }//end if
+
+ if (isset($tokens[$end]['parenthesis_closer']) === true) {
+ $end = $tokens[$end]['parenthesis_closer'];
+ $lastNonEmpty = $end;
+ continue;
+ }
+
+ if ($tokens[$end]['code'] !== T_WHITESPACE) {
+ $lastNonEmpty = $end;
+ }
+ }//end for
+
+ if ($end === $phpcsFile->numTokens) {
+ $end = $lastNonEmpty;
+ }
+
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true);
+
+ if ($next === false || $tokens[$next]['line'] !== $tokens[$end]['line']) {
+ // Looks for completely empty statements.
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($closer + 1), ($end + 1), true);
+
+ // Account for a comment on the end of the line.
+ for ($endLine = $end; $endLine < $phpcsFile->numTokens; $endLine++) {
+ if (isset($tokens[($endLine + 1)]) === false
+ || $tokens[$endLine]['line'] !== $tokens[($endLine + 1)]['line']
+ ) {
+ break;
+ }
+ }
+
+ if ($tokens[$endLine]['code'] !== T_COMMENT) {
+ $endLine = $end;
+ }
+ } else {
+ $next = ($end + 1);
+ $endLine = $end;
+ }
+
+ if ($next !== $end) {
+ if ($endLine !== $end) {
+ $endToken = $endLine;
+ $addedContent = '';
+ } else {
+ $endToken = $end;
+ $addedContent = $phpcsFile->eolChar;
+
+ if ($tokens[$end]['code'] !== T_SEMICOLON
+ && $tokens[$end]['code'] !== T_CLOSE_CURLY_BRACKET
+ ) {
+ $phpcsFile->fixer->addContent($end, '; ');
+ }
+ }
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($endToken + 1), null, true);
+ if ($next !== false
+ && ($tokens[$next]['code'] === T_ELSE
+ || $tokens[$next]['code'] === T_ELSEIF)
+ ) {
+ $phpcsFile->fixer->addContentBefore($next, '} ');
+ } else {
+ $indent = '';
+ for ($first = $stackPtr; $first > 0; $first--) {
+ if ($first === 1
+ || $tokens[($first - 1)]['line'] !== $tokens[$first]['line']
+ ) {
+ break;
+ }
+ }
+
+ if ($tokens[$first]['code'] === T_WHITESPACE) {
+ $indent = $tokens[$first]['content'];
+ } else if ($tokens[$first]['code'] === T_INLINE_HTML
+ || $tokens[$first]['code'] === T_OPEN_TAG
+ ) {
+ $addedContent = '';
+ }
+
+ $addedContent .= $indent.'}';
+ if ($next !== false && $tokens[$endToken]['code'] === T_COMMENT) {
+ $addedContent .= $phpcsFile->eolChar;
+ }
+
+ $phpcsFile->fixer->addContent($endToken, $addedContent);
+ }//end if
+ } else {
+ if ($endLine !== $end) {
+ $phpcsFile->fixer->replaceToken($end, '');
+ $phpcsFile->fixer->addNewlineBefore($endLine);
+ $phpcsFile->fixer->addContent($endLine, '}');
+ } else {
+ $phpcsFile->fixer->replaceToken($end, '}');
+ }
+ }//end if
+
+ $phpcsFile->fixer->endChangeset();
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Runs csslint on the file.
+ *
+ * @author Roman Levishchenko <index.0h@gmail.com>
+ * @copyright 2013-2014 Roman Levishchenko
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Debug;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Config;
+
+class CSSLintSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $csslintPath = Config::getExecutablePath('csslint');
+ if ($csslintPath === null) {
+ return;
+ }
+
+ $fileName = $phpcsFile->getFilename();
+
+ $cmd = escapeshellcmd($csslintPath).' '.escapeshellarg($fileName).' 2>&1';
+ exec($cmd, $output, $retval);
+
+ if (is_array($output) === false) {
+ return;
+ }
+
+ $count = count($output);
+
+ for ($i = 0; $i < $count; $i++) {
+ $matches = array();
+ $numMatches = preg_match(
+ '/(error|warning) at line (\d+)/',
+ $output[$i],
+ $matches
+ );
+
+ if ($numMatches === 0) {
+ continue;
+ }
+
+ $line = (int) $matches[2];
+ $message = 'csslint says: '.$output[($i + 1)];
+ // First line is message with error line and error code.
+ // Second is error message.
+ // Third is wrong line in file.
+ // Fourth is empty line.
+ $i += 4;
+
+ $phpcsFile->addWarningOnLine($message, $line, 'ExternalTool');
+ }//end for
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Runs gjslint on the file.
+ *
+ * @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\Standards\Generic\Sniffs\Debug;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Config;
+
+class ClosureLinterSniff implements Sniff
+{
+
+ /**
+ * A list of error codes that should show errors.
+ *
+ * All other error codes will show warnings.
+ *
+ * @var integer
+ */
+ public $errorCodes = array();
+
+ /**
+ * A list of error codes to ignore.
+ *
+ * @var integer
+ */
+ public $ignoreCodes = array();
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jslint.js could not be run
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $lintPath = Config::getExecutablePath('gjslint');
+ if ($lintPath === null) {
+ return;
+ }
+
+ $fileName = $phpcsFile->getFilename();
+
+ $lintPath = escapeshellcmd($lintPath);
+ $cmd = '$lintPath --nosummary --notime --unix_mode '.escapeshellarg($fileName);
+ $msg = exec($cmd, $output, $retval);
+
+ if (is_array($output) === false) {
+ return;
+ }
+
+ foreach ($output as $finding) {
+ $matches = array();
+ $numMatches = preg_match('/^(.*):([0-9]+):\(.*?([0-9]+)\)(.*)$/', $finding, $matches);
+ if ($numMatches === 0) {
+ continue;
+ }
+
+ // Skip error codes we are ignoring.
+ $code = $matches[3];
+ if (in_array($code, $this->ignoreCodes) === true) {
+ continue;
+ }
+
+ $line = (int) $matches[2];
+ $error = trim($matches[4]);
+
+ $message = 'gjslint says: (%s) %s';
+ $data = array(
+ $code,
+ $error,
+ );
+ if (in_array($code, $this->errorCodes) === true) {
+ $phpcsFile->addErrorOnLine($message, $line, 'ExternalToolError', $data);
+ } else {
+ $phpcsFile->addWarningOnLine($message, $line, 'ExternalTool', $data);
+ }
+ }//end foreach
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Runs eslint on the file.
+ *
+ * @author Ryan McCue <ryan+gh@hmn.md>
+ * @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\Standards\Generic\Sniffs\Debug;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Config;
+
+class ESLintSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * ESLint configuration file path.
+ *
+ * @var string|null Path to eslintrc. Null to autodetect.
+ */
+ public $configFile = null;
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jshint.js could not be run
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $eslintPath = Config::getExecutablePath('eslint');
+ if ($eslintPath === null) {
+ return;
+ }
+
+ $filename = $phpcsFile->getFilename();
+
+ $configFile = $this->configFile;
+ if (empty($configFile) === true) {
+ // Attempt to autodetect.
+ $candidates = glob('.eslintrc{.js,.yaml,.yml,.json}', GLOB_BRACE);
+ if (empty($candidates) === false) {
+ $configFile = $candidates[0];
+ }
+ }
+
+ $eslintOptions = array('--format json');
+ if (empty($configFile) === false) {
+ $eslintOptions[] = '--config '.escapeshellarg($configFile);
+ }
+
+ $cmd = escapeshellcmd(escapeshellarg($eslintPath).' '.implode(' ', $eslintOptions).' '.escapeshellarg($filename));
+
+ // Execute!
+ exec($cmd, $stdout, $code);
+
+ if ($code <= 0) {
+ // No errors, continue.
+ return ($phpcsFile->numTokens + 1);
+ }
+
+ $data = json_decode(implode("\n", $stdout));
+ if (json_last_error() !== JSON_ERROR_NONE) {
+ // Ignore any errors.
+ return ($phpcsFile->numTokens + 1);
+ }
+
+ // Data is a list of files, but we only pass a single one.
+ $messages = $data[0]->messages;
+ foreach ($messages as $error) {
+ $message = 'eslint says: '.$error->message;
+ if (empty($error->fatal) === false || $error->severity === 2) {
+ $phpcsFile->addErrorOnLine($message, $error->line, 'ExternalTool');
+ } else {
+ $phpcsFile->addWarningOnLine($message, $error->line, 'ExternalTool');
+ }
+ }
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Runs jshint.js on the file.
+ *
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @author Alexander Wei§ <aweisswa@gmx.de>
+ * @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\Standards\Generic\Sniffs\Debug;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Config;
+
+class JSHintSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jshint.js could not be run
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $rhinoPath = Config::getExecutablePath('rhino');
+ $jshintPath = Config::getExecutablePath('jshint');
+ if ($rhinoPath === null || $jshintPath === null) {
+ return;
+ }
+
+ $fileName = $phpcsFile->getFilename();
+
+ $rhinoPath = escapeshellcmd($rhinoPath);
+ $jshintPath = escapeshellcmd($jshintPath);
+
+ $cmd = "$rhinoPath \"$jshintPath\" ".escapeshellarg($fileName);
+ $msg = exec($cmd, $output, $retval);
+
+ if (is_array($output) === true) {
+ foreach ($output as $finding) {
+ $matches = array();
+ $numMatches = preg_match('/^(.+)\(.+:([0-9]+).*:[0-9]+\)$/', $finding, $matches);
+ if ($numMatches === 0) {
+ continue;
+ }
+
+ $line = (int) $matches[2];
+ $message = 'jshint says: '.trim($matches[1]);
+ $phpcsFile->addWarningOnLine($message, $line, 'ExternalTool');
+ }
+ }
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * A simple sniff for detecting BOMs that may corrupt application work.
+ *
+ * @author Piotr Karas <office@mediaself.pl>
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @copyright 2010-2014 mediaSELF Sp. z o.o.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ByteOrderMarkSniff implements Sniff
+{
+
+ /**
+ * List of supported BOM definitions.
+ *
+ * Use encoding names as keys and hex BOM representations as values.
+ *
+ * @var array
+ */
+ protected $bomDefinitions = array(
+ 'UTF-8' => 'efbbbf',
+ 'UTF-16 (BE)' => 'feff',
+ 'UTF-16 (LE)' => 'fffe',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_INLINE_HTML);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ // The BOM will be the very first token in the file.
+ if ($stackPtr !== 0) {
+ return;
+ }
+
+ $tokens = $phpcsFile->getTokens();
+
+ foreach ($this->bomDefinitions as $bomName => $expectedBomHex) {
+ $bomByteLength = (strlen($expectedBomHex) / 2);
+ $htmlBomHex = bin2hex(substr($tokens[$stackPtr]['content'], 0, $bomByteLength));
+ if ($htmlBomHex === $expectedBomHex) {
+ $errorData = array($bomName);
+ $error = 'File contains %s byte order mark, which may corrupt your application';
+ $phpcsFile->addError($error, $stackPtr, 'Found', $errorData);
+ $phpcsFile->recordMetric($stackPtr, 'Using byte order mark', 'yes');
+ return;
+ }
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Using byte order mark', 'no');
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures the file ends with a newline character.
+ *
+ * @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\Standards\Generic\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class EndFileNewlineSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ 'CSS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return int
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ // Skip to the end of the file.
+ $tokens = $phpcsFile->getTokens();
+ $stackPtr = ($phpcsFile->numTokens - 1);
+
+ if ($tokens[$stackPtr]['content'] === '') {
+ $stackPtr--;
+ }
+
+ $eolCharLen = strlen($phpcsFile->eolChar);
+ $lastChars = substr($tokens[$stackPtr]['content'], ($eolCharLen * -1));
+ if ($lastChars !== $phpcsFile->eolChar) {
+ $phpcsFile->recordMetric($stackPtr, 'Newline at EOF', 'no');
+
+ $error = 'File must end with a newline character';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotFound');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewline($stackPtr);
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Newline at EOF', 'yes');
+ }
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures the file does not end with a newline character.
+ *
+ * @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\Standards\Generic\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class EndFileNoNewlineSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ 'CSS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return int
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ // Skip to the end of the file.
+ $tokens = $phpcsFile->getTokens();
+ $stackPtr = ($phpcsFile->numTokens - 1);
+
+ if ($tokens[$stackPtr]['content'] === '') {
+ $stackPtr--;
+ }
+
+ $eolCharLen = strlen($phpcsFile->eolChar);
+ $lastChars = substr($tokens[$stackPtr]['content'], ($eolCharLen * -1));
+ if ($lastChars === $phpcsFile->eolChar) {
+ $error = 'File must not end with a newline character';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
+ if ($fix === true) {
+ $newContent = substr($tokens[$stackPtr]['content'], 0, ($eolCharLen * -1));
+ $phpcsFile->fixer->replaceToken($stackPtr, $newContent);
+ }
+ }
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures the whole file is PHP only, with no whitespace or inline HTML.
+ *
+ * @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\Standards\Generic\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class InlineHTMLSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_INLINE_HTML);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ // Ignore shebang lines.
+ $tokens = $phpcsFile->getTokens();
+ if (substr($tokens[$stackPtr]['content'], 0, 2) === '#!') {
+ return;
+ }
+
+ $error = 'PHP files must only contain PHP code';
+ $phpcsFile->addError($error, $stackPtr, 'Found');
+
+ return $phpcsFile->numTokens;
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that end of line characters are correct.
+ *
+ * @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\Standards\Generic\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class LineEndingsSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ 'CSS',
+ );
+
+ /**
+ * The valid EOL character.
+ *
+ * @var string
+ */
+ public $eolChar = '\n';
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return int
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $found = $phpcsFile->eolChar;
+ $found = str_replace("\n", '\n', $found);
+ $found = str_replace("\r", '\r', $found);
+
+ $phpcsFile->recordMetric($stackPtr, 'EOL char', $found);
+
+ if ($found === $this->eolChar) {
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+ }
+
+ // Check for single line files without an EOL. This is a very special
+ // case and the EOL char is set to \n when this happens.
+ if ($found === '\n') {
+ $tokens = $phpcsFile->getTokens();
+ $lastToken = ($phpcsFile->numTokens - 1);
+ if ($tokens[$lastToken]['line'] === 1
+ && $tokens[$lastToken]['content'] !== "\n"
+ ) {
+ return;
+ }
+ }
+
+ $error = 'End of line character is invalid; expected "%s" but found "%s"';
+ $expected = $this->eolChar;
+ $expected = str_replace("\n", '\n', $expected);
+ $expected = str_replace("\r", '\r', $expected);
+ $data = array(
+ $expected,
+ $found,
+ );
+
+ // Errors are always reported on line 1, no matter where the first PHP tag is.
+ $fix = $phpcsFile->addFixableError($error, 0, 'InvalidEOLChar', $data);
+
+ if ($fix === true) {
+ $tokens = $phpcsFile->getTokens();
+ switch ($this->eolChar) {
+ case '\n':
+ $eolChar = "\n";
+ break;
+ case '\r':
+ $eolChar = "\r";
+ break;
+ case '\r\n':
+ $eolChar = "\r\n";
+ break;
+ default:
+ $eolChar = $this->eolChar;
+ break;
+ }
+
+ for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
+ if (isset($tokens[($i + 1)]) === false
+ || $tokens[($i + 1)]['line'] > $tokens[$i]['line']
+ ) {
+ // Token is the last on a line.
+ if (isset($tokens[$i]['orig_content']) === true) {
+ $tokenContent = $tokens[$i]['orig_content'];
+ } else {
+ $tokenContent = $tokens[$i]['content'];
+ }
+
+ $newContent = rtrim($tokenContent, "\r\n");
+ $newContent .= $eolChar;
+ $phpcsFile->fixer->replaceToken($i, $newContent);
+ }
+ }
+ }//end if
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the length of all lines in a file.
+ *
+ * Checks all lines in the file, and throws warnings if they are over 80
+ * characters in length and errors if they are over 100. Both these
+ * figures can be changed in a ruleset.xml file.
+ *
+ * @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\Standards\Generic\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class LineLengthSniff implements Sniff
+{
+
+ /**
+ * The limit that the length of a line should not exceed.
+ *
+ * @var integer
+ */
+ public $lineLimit = 80;
+
+ /**
+ * The limit that the length of a line must not exceed.
+ *
+ * Set to zero (0) to disable.
+ *
+ * @var integer
+ */
+ public $absoluteLineLimit = 100;
+
+ /**
+ * Whether or not to ignore comment lines.
+ *
+ * @var boolean
+ */
+ public $ignoreComments = false;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return int
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ for ($i = 1; $i < $phpcsFile->numTokens; $i++) {
+ if ($tokens[$i]['column'] === 1) {
+ $this->checkLineLength($phpcsFile, $tokens, $i);
+ }
+ }
+
+ $this->checkLineLength($phpcsFile, $tokens, $i);
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+ /**
+ * Checks if a line is too long.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $tokens The token stack.
+ * @param int $stackPtr The first token on the next line.
+ *
+ * @return null|false
+ */
+ protected function checkLineLength($phpcsFile, $tokens, $stackPtr)
+ {
+ // The passed token is the first on the line.
+ $stackPtr--;
+
+ if ($tokens[$stackPtr]['column'] === 1
+ && $tokens[$stackPtr]['length'] === 0
+ ) {
+ // Blank line.
+ return;
+ }
+
+ if ($tokens[$stackPtr]['column'] !== 1
+ && $tokens[$stackPtr]['content'] === $phpcsFile->eolChar
+ ) {
+ $stackPtr--;
+ }
+
+ $lineLength = ($tokens[$stackPtr]['column'] + $tokens[$stackPtr]['length'] - 1);
+
+ // Record metrics for common line length groupings.
+ if ($lineLength <= 80) {
+ $phpcsFile->recordMetric($stackPtr, 'Line length', '80 or less');
+ } else if ($lineLength <= 120) {
+ $phpcsFile->recordMetric($stackPtr, 'Line length', '81-120');
+ } else if ($lineLength <= 150) {
+ $phpcsFile->recordMetric($stackPtr, 'Line length', '121-150');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Line length', '151 or more');
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_COMMENT
+ || $tokens[$stackPtr]['code'] === T_DOC_COMMENT_STRING
+ ) {
+ if ($this->ignoreComments === true) {
+ return;
+ }
+
+ // If this is a long comment, check if it can be broken up onto multiple lines.
+ // Some comments contain unbreakable strings like URLs and so it makes sense
+ // to ignore the line length in these cases if the URL would be longer than the max
+ // line length once you indent it to the correct level.
+ if ($lineLength > $this->lineLimit) {
+ $oldLength = strlen($tokens[$stackPtr]['content']);
+ $newLength = strlen(ltrim($tokens[$stackPtr]['content'], "/#\t "));
+ $indent = (($tokens[$stackPtr]['column'] - 1) + ($oldLength - $newLength));
+
+ $nonBreakingLength = $tokens[$stackPtr]['length'];
+
+ $space = strrpos($tokens[$stackPtr]['content'], ' ');
+ if ($space !== false) {
+ $nonBreakingLength -= ($space + 1);
+ }
+
+ if (($nonBreakingLength + $indent) > $this->lineLimit) {
+ return;
+ }
+ }
+ }//end if
+
+ if ($this->absoluteLineLimit > 0
+ && $lineLength > $this->absoluteLineLimit
+ ) {
+ $data = array(
+ $this->absoluteLineLimit,
+ $lineLength,
+ );
+
+ $error = 'Line exceeds maximum limit of %s characters; contains %s characters';
+ $phpcsFile->addError($error, $stackPtr, 'MaxExceeded', $data);
+ } else if ($lineLength > $this->lineLimit) {
+ $data = array(
+ $this->lineLimit,
+ $lineLength,
+ );
+
+ $warning = 'Line exceeds %s characters; contains %s characters';
+ $phpcsFile->addWarning($warning, $stackPtr, 'TooLong', $data);
+ }
+
+ }//end checkLineLength()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that all file names are lowercased.
+ *
+ * @author Andy Grunwald <andygrunwald@gmail.com>
+ * @copyright 2010-2014 Andy Grunwald
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class LowercasedFilenameSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return int
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $filename = $phpcsFile->getFilename();
+ if ($filename === 'STDIN') {
+ return;
+ }
+
+ $filename = basename($filename);
+ $lowercaseFilename = strtolower($filename);
+ if ($filename !== $lowercaseFilename) {
+ $data = array(
+ $filename,
+ $lowercaseFilename,
+ );
+ $error = 'Filename "%s" doesn\'t match the expected filename "%s"';
+ $phpcsFile->addError($error, $stackPtr, 'NotFound', $data);
+ $phpcsFile->recordMetric($stackPtr, 'Lowercase filename', 'no');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Lowercase filename', 'yes');
+ }
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that only one class is declared per file.
+ *
+ * @author Andy Grunwald <andygrunwald@gmail.com>
+ * @copyright 2010-2014 Andy Grunwald
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class OneClassPerFileSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_CLASS);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $nextClass = $phpcsFile->findNext($this->register(), ($stackPtr + 1));
+ if ($nextClass !== false) {
+ $error = 'Only one class is allowed in a file';
+ $phpcsFile->addError($error, $nextClass, 'MultipleFound');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that only one interface is declared per file.
+ *
+ * @author Andy Grunwald <andygrunwald@gmail.com>
+ * @copyright 2010-2014 Andy Grunwald
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class OneInterfacePerFileSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_INTERFACE);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $nextInterface = $phpcsFile->findNext($this->register(), ($stackPtr + 1));
+ if ($nextInterface !== false) {
+ $error = 'Only one interface is allowed in a file';
+ $phpcsFile->addError($error, $nextInterface, 'MultipleFound');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that only one object structure is declared per file.
+ *
+ * @author Mponos George <gmponos@gmail.com>
+ * @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\Standards\Generic\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class OneObjectStructurePerFileSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_CLASS,
+ T_INTERFACE,
+ T_TRAIT,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $nextClass = $phpcsFile->findNext($this->register(), ($stackPtr + 1));
+ if ($nextClass !== false) {
+ $error = 'Only one object structure is allowed in a file';
+ $phpcsFile->addError($error, $nextClass, 'MultipleFound');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that only one trait is declared per file.
+ *
+ * @author Alexander Obuhovich <aik.bold@gmail.com>
+ * @copyright 2010-2014 Alexander Obuhovich
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class OneTraitPerFileSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_TRAIT);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $nextClass = $phpcsFile->findNext($this->register(), ($stackPtr + 1));
+ if ($nextClass !== false) {
+ $error = 'Only one trait is allowed in a file';
+ $phpcsFile->addError($error, $nextClass, 'MultipleFound');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures each statement is on a line by itself.
+ *
+ * @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\Standards\Generic\Sniffs\Formatting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DisallowMultipleStatementsSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_SEMICOLON);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $prev = $phpcsFile->findPrevious(array(T_SEMICOLON, T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO), ($stackPtr - 1));
+ if ($prev === false
+ || $tokens[$prev]['code'] === T_OPEN_TAG
+ || $tokens[$prev]['code'] === T_OPEN_TAG_WITH_ECHO
+ ) {
+ $phpcsFile->recordMetric($stackPtr, 'Multiple statements on same line', 'no');
+ return;
+ }
+
+ // Ignore multiple statements in a FOR condition.
+ if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
+ foreach ($tokens[$stackPtr]['nested_parenthesis'] as $bracket) {
+ if (isset($tokens[$bracket]['parenthesis_owner']) === false) {
+ // Probably a closure sitting inside a function call.
+ continue;
+ }
+
+ $owner = $tokens[$bracket]['parenthesis_owner'];
+ if ($tokens[$owner]['code'] === T_FOR) {
+ return;
+ }
+ }
+ }
+
+ if ($tokens[$prev]['line'] === $tokens[$stackPtr]['line']) {
+ $phpcsFile->recordMetric($stackPtr, 'Multiple statements on same line', 'yes');
+
+ $error = 'Each PHP statement must be on a line by itself';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SameLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->addNewline($prev);
+ if ($tokens[($prev + 1)]['code'] === T_WHITESPACE) {
+ $phpcsFile->fixer->replaceToken(($prev + 1), '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Multiple statements on same line', 'no');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks alignment of assignments.
+ *
+ * If there are multiple adjacent assignments, it will check that the equals signs of
+ * each assignment are aligned. It will display a warning to advise that the signs should be aligned.
+ *
+ * @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\Standards\Generic\Sniffs\Formatting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class MultipleStatementAlignmentSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+ /**
+ * If true, an error will be thrown; otherwise a warning.
+ *
+ * @var boolean
+ */
+ public $error = false;
+
+ /**
+ * The maximum amount of padding before the alignment is ignored.
+ *
+ * If the amount of padding required to align this assignment with the
+ * surrounding assignments exceeds this number, the assignment will be
+ * ignored and no errors or warnings will be thrown.
+ *
+ * @var integer
+ */
+ public $maxPadding = 1000;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ $tokens = Tokens::$assignmentTokens;
+ unset($tokens[T_DOUBLE_ARROW]);
+ return $tokens;
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return int
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Ignore assignments used in a condition, like an IF or FOR.
+ if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
+ foreach ($tokens[$stackPtr]['nested_parenthesis'] as $start => $end) {
+ if (isset($tokens[$start]['parenthesis_owner']) === true) {
+ return;
+ }
+ }
+ }
+
+ $lastAssign = $this->checkAlignment($phpcsFile, $stackPtr);
+ return ($lastAssign + 1);
+
+ }//end process()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return int
+ */
+ public function checkAlignment($phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $assignments = array();
+ $prevAssign = null;
+ $lastLine = $tokens[$stackPtr]['line'];
+ $maxPadding = null;
+ $stopped = null;
+ $lastCode = $stackPtr;
+ $lastSemi = null;
+
+ $find = Tokens::$assignmentTokens;
+ unset($find[T_DOUBLE_ARROW]);
+
+ for ($assign = $stackPtr; $assign < $phpcsFile->numTokens; $assign++) {
+ if (isset($find[$tokens[$assign]['code']]) === false) {
+ // A blank line indicates that the assignment block has ended.
+ if (isset(Tokens::$emptyTokens[$tokens[$assign]['code']]) === false) {
+ if (($tokens[$assign]['line'] - $tokens[$lastCode]['line']) > 1) {
+ break;
+ }
+
+ $lastCode = $assign;
+
+ if ($tokens[$assign]['code'] === T_SEMICOLON) {
+ if ($tokens[$assign]['conditions'] === $tokens[$stackPtr]['conditions']) {
+ if ($lastSemi !== null && $prevAssign !== null && $lastSemi > $prevAssign) {
+ // This statement did not have an assignment operator in it.
+ break;
+ } else {
+ $lastSemi = $assign;
+ }
+ } else {
+ // Statement is in a different context, so the block is over.
+ break;
+ }
+ }
+ }//end if
+
+ continue;
+ } else if ($assign !== $stackPtr && $tokens[$assign]['line'] === $lastLine) {
+ // Skip multiple assignments on the same line. We only need to
+ // try and align the first assignment.
+ continue;
+ }//end if
+
+ if ($assign !== $stackPtr) {
+ // Has to be nested inside the same conditions as the first assignment.
+ if ($tokens[$assign]['conditions'] !== $tokens[$stackPtr]['conditions']) {
+ break;
+ }
+
+ // Make sure it is not assigned inside a condition (eg. IF, FOR).
+ if (isset($tokens[$assign]['nested_parenthesis']) === true) {
+ foreach ($tokens[$assign]['nested_parenthesis'] as $start => $end) {
+ if (isset($tokens[$start]['parenthesis_owner']) === true) {
+ break(2);
+ }
+ }
+ }
+ }//end if
+
+ $var = $phpcsFile->findPrevious(
+ Tokens::$emptyTokens,
+ ($assign - 1),
+ null,
+ true
+ );
+
+ // Make sure we wouldn't break our max padding length if we
+ // aligned with this statement, or they wouldn't break the max
+ // padding length if they aligned with us.
+ $varEnd = $tokens[($var + 1)]['column'];
+ $assignLen = $tokens[$assign]['length'];
+ if ($assign !== $stackPtr) {
+ if (($varEnd + 1) > $assignments[$prevAssign]['assign_col']) {
+ $padding = 1;
+ $assignColumn = ($varEnd + 1);
+ } else {
+ $padding = ($assignments[$prevAssign]['assign_col'] - $varEnd + $assignments[$prevAssign]['assign_len'] - $assignLen);
+ if ($padding === 0) {
+ $padding = 1;
+ }
+
+ if ($padding > $this->maxPadding) {
+ $stopped = $assign;
+ break;
+ }
+
+ $assignColumn = ($varEnd + $padding);
+ }//end if
+
+ if (($assignColumn + $assignLen) > ($assignments[$maxPadding]['assign_col'] + $assignments[$maxPadding]['assign_len'])) {
+ $newPadding = ($varEnd - $assignments[$maxPadding]['var_end'] + $assignLen - $assignments[$maxPadding]['assign_len'] + 1);
+ if ($newPadding > $this->maxPadding) {
+ $stopped = $assign;
+ break;
+ } else {
+ // New alignment settings for previous assignments.
+ foreach ($assignments as $i => $data) {
+ if ($i === $assign) {
+ break;
+ }
+
+ $newPadding = ($varEnd - $data['var_end'] + $assignLen - $data['assign_len'] + 1);
+ $assignments[$i]['expected'] = $newPadding;
+ $assignments[$i]['assign_col'] = ($data['var_end'] + $newPadding);
+ }
+
+ $padding = 1;
+ $assignColumn = ($varEnd + 1);
+ }
+ } else if ($padding > $assignments[$maxPadding]['expected']) {
+ $maxPadding = $assign;
+ }//end if
+ } else {
+ $padding = 1;
+ $assignColumn = ($varEnd + 1);
+ $maxPadding = $assign;
+ }//end if
+
+ $found = 0;
+ if ($tokens[($var + 1)]['code'] === T_WHITESPACE) {
+ $found = $tokens[($var + 1)]['length'];
+ if ($found === 0) {
+ // This means a newline was found.
+ $found = 1;
+ }
+ }
+
+ $assignments[$assign] = array(
+ 'var_end' => $varEnd,
+ 'assign_len' => $assignLen,
+ 'assign_col' => $assignColumn,
+ 'expected' => $padding,
+ 'found' => $found,
+ );
+
+ $lastLine = $tokens[$assign]['line'];
+ $prevAssign = $assign;
+ }//end for
+
+ if (empty($assignments) === true) {
+ return $stackPtr;
+ }
+
+ $numAssignments = count($assignments);
+
+ $errorGenerated = false;
+ foreach ($assignments as $assignment => $data) {
+ if ($data['found'] === $data['expected']) {
+ continue;
+ }
+
+ $expectedText = $data['expected'].' space';
+ if ($data['expected'] !== 1) {
+ $expectedText .= 's';
+ }
+
+ if ($data['found'] === null) {
+ $foundText = 'a new line';
+ } else {
+ $foundText = $data['found'].' space';
+ if ($data['found'] !== 1) {
+ $foundText .= 's';
+ }
+ }
+
+ if ($numAssignments === 1) {
+ $type = 'Incorrect';
+ $error = 'Equals sign not aligned correctly; expected %s but found %s';
+ } else {
+ $type = 'NotSame';
+ $error = 'Equals sign not aligned with surrounding assignments; expected %s but found %s';
+ }
+
+ $errorData = array(
+ $expectedText,
+ $foundText,
+ );
+
+ if ($this->error === true) {
+ $fix = $phpcsFile->addFixableError($error, $assignment, $type, $errorData);
+ } else {
+ $fix = $phpcsFile->addFixableWarning($error, $assignment, $type.'Warning', $errorData);
+ }
+
+ $errorGenerated = true;
+
+ if ($fix === true && $data['found'] !== null) {
+ $newContent = str_repeat(' ', $data['expected']);
+ if ($data['found'] === 0) {
+ $phpcsFile->fixer->addContentBefore($assignment, $newContent);
+ } else {
+ $phpcsFile->fixer->replaceToken(($assignment - 1), $newContent);
+ }
+ }
+ }//end foreach
+
+ if ($numAssignments > 1) {
+ if ($errorGenerated === true) {
+ $phpcsFile->recordMetric($stackPtr, 'Adjacent assignments aligned', 'no');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Adjacent assignments aligned', 'yes');
+ }
+ }
+
+ if ($stopped !== null) {
+ return $this->checkAlignment($phpcsFile, $stopped);
+ } else {
+ return $assignment;
+ }
+
+ }//end checkAlignment()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures there is no space after cast tokens.
+ *
+ * @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\Standards\Generic\Sniffs\Formatting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class NoSpaceAfterCastSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return Tokens::$castTokens;
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 + 1)]['code'] !== T_WHITESPACE) {
+ return;
+ }
+
+ $error = 'A cast statement must not be followed by a space';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceFound');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), '');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures there is a single space after cast tokens.
+ *
+ * @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\Standards\Generic\Sniffs\Formatting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class SpaceAfterCastSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return Tokens::$castTokens;
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 + 1)]['code'] !== T_WHITESPACE) {
+ $error = 'A cast statement must be followed by a single space';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpace');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($stackPtr, ' ');
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Spacing after cast statement', 0);
+ return;
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Spacing after cast statement', $tokens[($stackPtr + 1)]['length']);
+
+ if ($tokens[($stackPtr + 1)]['length'] !== 1) {
+ $error = 'A cast statement must be followed by a single space';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'TooMuchSpace');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures there is a single space after a NOT operator.
+ *
+ * @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\Standards\Generic\Sniffs\Formatting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class SpaceAfterNotSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_BOOLEAN_NOT);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $spacing = 0;
+ if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
+ $spacing = $tokens[($stackPtr + 1)]['length'];
+ }
+
+ if ($spacing === 1) {
+ return;
+ }
+
+ $message = 'There must be a single space after a NOT operator; %s found';
+ $fix = $phpcsFile->addFixableError($message, $stackPtr, 'Incorrect', array($spacing));
+
+ if ($fix === true) {
+ if ($spacing === 0) {
+ $phpcsFile->fixer->addContent($stackPtr, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that variables are not passed by reference when calling a function.
+ *
+ * @author Florian Grandel <jerico.dev@gmail.com>
+ * @copyright 2009-2014 Florian Grandel
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Functions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class CallTimePassByReferenceSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_STRING,
+ T_VARIABLE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $findTokens = array_merge(
+ Tokens::$emptyTokens,
+ array(T_BITWISE_AND)
+ );
+
+ $prev = $phpcsFile->findPrevious($findTokens, ($stackPtr - 1), null, true);
+
+ // Skip tokens that are the names of functions or classes
+ // within their definitions. For example: function myFunction...
+ // "myFunction" is T_STRING but we should skip because it is not a
+ // function or method *call*.
+ $prevCode = $tokens[$prev]['code'];
+ if ($prevCode === T_FUNCTION || $prevCode === T_CLASS) {
+ return;
+ }
+
+ // If the next non-whitespace token after the function or method call
+ // is not an opening parenthesis then it cant really be a *call*.
+ $functionName = $stackPtr;
+ $openBracket = $phpcsFile->findNext(
+ Tokens::$emptyTokens,
+ ($functionName + 1),
+ null,
+ true
+ );
+
+ if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
+ return;
+ }
+
+ if (isset($tokens[$openBracket]['parenthesis_closer']) === false) {
+ return;
+ }
+
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+
+ $nextSeparator = $openBracket;
+ $find = array(
+ T_VARIABLE,
+ T_OPEN_SHORT_ARRAY,
+ );
+
+ while (($nextSeparator = $phpcsFile->findNext($find, ($nextSeparator + 1), $closeBracket)) !== false) {
+ if (isset($tokens[$nextSeparator]['nested_parenthesis']) === false) {
+ continue;
+ }
+
+ if ($tokens[$nextSeparator]['code'] === T_OPEN_SHORT_ARRAY) {
+ $nextSeparator = $tokens[$nextSeparator]['bracket_closer'];
+ continue;
+ }
+
+ // Make sure the variable belongs directly to this function call
+ // and is not inside a nested function call or array.
+ $brackets = $tokens[$nextSeparator]['nested_parenthesis'];
+ $lastBracket = array_pop($brackets);
+ if ($lastBracket !== $closeBracket) {
+ continue;
+ }
+
+ // Checking this: $value = my_function(...[*]$arg...).
+ $tokenBefore = $phpcsFile->findPrevious(
+ Tokens::$emptyTokens,
+ ($nextSeparator - 1),
+ null,
+ true
+ );
+
+ if ($tokens[$tokenBefore]['code'] === T_BITWISE_AND) {
+ // Checking this: $value = my_function(...[*]&$arg...).
+ $tokenBefore = $phpcsFile->findPrevious(
+ Tokens::$emptyTokens,
+ ($tokenBefore - 1),
+ null,
+ true
+ );
+
+ // We have to exclude all uses of T_BITWISE_AND that are not
+ // references. We use a blacklist approach as we prefer false
+ // positives to not identifying a pass-by-reference call at all.
+ $tokenCode = $tokens[$tokenBefore]['code'];
+ if ($tokenCode === T_VARIABLE
+ || $tokenCode === T_CLOSE_PARENTHESIS
+ || $tokenCode === T_CLOSE_SQUARE_BRACKET
+ || $tokenCode === T_LNUMBER
+ || isset(Tokens::$assignmentTokens[$tokenCode]) === true
+ ) {
+ continue;
+ }
+
+ // T_BITWISE_AND represents a pass-by-reference.
+ $error = 'Call-time pass-by-reference calls are prohibited';
+ $phpcsFile->addError($error, $tokenBefore, 'NotAllowed');
+ }//end if
+ }//end while
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that calls to methods and functions are spaced correctly.
+ *
+ * @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\Standards\Generic\Sniffs\Functions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class FunctionCallArgumentSpacingSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ $tokens = Tokens::$functionNameTokens;
+
+ $tokens[] = T_VARIABLE;
+ $tokens[] = T_CLOSE_CURLY_BRACKET;
+ $tokens[] = T_CLOSE_PARENTHESIS;
+
+ return $tokens;
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // Skip tokens that are the names of functions or classes
+ // within their definitions. For example:
+ // function myFunction...
+ // "myFunction" is T_STRING but we should skip because it is not a
+ // function or method *call*.
+ $functionName = $stackPtr;
+ $ignoreTokens = Tokens::$emptyTokens;
+ $ignoreTokens[] = T_BITWISE_AND;
+ $functionKeyword = $phpcsFile->findPrevious($ignoreTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$functionKeyword]['code'] === T_FUNCTION || $tokens[$functionKeyword]['code'] === T_CLASS) {
+ return;
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_CLOSE_CURLY_BRACKET
+ && isset($tokens[$stackPtr]['scope_condition']) === true
+ ) {
+ // Not a function call.
+ return;
+ }
+
+ // If the next non-whitespace token after the function or method call
+ // is not an opening parenthesis then it cant really be a *call*.
+ $openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($functionName + 1), null, true);
+ if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
+ return;
+ }
+
+ if (isset($tokens[$openBracket]['parenthesis_closer']) === false) {
+ return;
+ }
+
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+ $nextSeparator = $openBracket;
+
+ $find = array(
+ T_COMMA,
+ T_VARIABLE,
+ T_CLOSURE,
+ T_OPEN_SHORT_ARRAY,
+ );
+
+ while (($nextSeparator = $phpcsFile->findNext($find, ($nextSeparator + 1), $closeBracket)) !== false) {
+ if ($tokens[$nextSeparator]['code'] === T_CLOSURE) {
+ // Skip closures.
+ $nextSeparator = $tokens[$nextSeparator]['scope_closer'];
+ continue;
+ } else if ($tokens[$nextSeparator]['code'] === T_OPEN_SHORT_ARRAY) {
+ // Skips arrays using short notation.
+ $nextSeparator = $tokens[$nextSeparator]['bracket_closer'];
+ continue;
+ }
+
+ // Make sure the comma or variable belongs directly to this function call,
+ // and is not inside a nested function call or array.
+ $brackets = $tokens[$nextSeparator]['nested_parenthesis'];
+ $lastBracket = array_pop($brackets);
+ if ($lastBracket !== $closeBracket) {
+ continue;
+ }
+
+ if ($tokens[$nextSeparator]['code'] === T_COMMA) {
+ if ($tokens[($nextSeparator - 1)]['code'] === T_WHITESPACE) {
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($nextSeparator - 2), null, true);
+ if (isset(Tokens::$heredocTokens[$tokens[$prev]['code']]) === false) {
+ $error = 'Space found before comma in function call';
+ $fix = $phpcsFile->addFixableError($error, $nextSeparator, 'SpaceBeforeComma');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($nextSeparator - 1), '');
+ }
+ }
+ }
+
+ if ($tokens[($nextSeparator + 1)]['code'] !== T_WHITESPACE) {
+ $error = 'No space found after comma in function call';
+ $fix = $phpcsFile->addFixableError($error, $nextSeparator, 'NoSpaceAfterComma');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($nextSeparator, ' ');
+ }
+ } else {
+ // If there is a newline in the space, then they must be formatting
+ // each argument on a newline, which is valid, so ignore it.
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextSeparator + 1), null, true);
+ if ($tokens[$next]['line'] === $tokens[$nextSeparator]['line']) {
+ $space = strlen($tokens[($nextSeparator + 1)]['content']);
+ if ($space > 1) {
+ $error = 'Expected 1 space after comma in function call; %s found';
+ $data = array($space);
+ $fix = $phpcsFile->addFixableError($error, $nextSeparator, 'TooMuchSpaceAfterComma', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($nextSeparator + 1), ' ');
+ }
+ }
+ }
+ }//end if
+ } else {
+ // Token is a variable.
+ $nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextSeparator + 1), $closeBracket, true);
+ if ($nextToken !== false) {
+ if ($tokens[$nextToken]['code'] === T_EQUAL) {
+ if (($tokens[($nextToken - 1)]['code']) !== T_WHITESPACE) {
+ $error = 'Expected 1 space before = sign of default value';
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'NoSpaceBeforeEquals');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContentBefore($nextToken, ' ');
+ }
+ }
+
+ if ($tokens[($nextToken + 1)]['code'] !== T_WHITESPACE) {
+ $error = 'Expected 1 space after = sign of default value';
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'NoSpaceAfterEquals');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($nextToken, ' ');
+ }
+ }
+ }
+ }
+ }//end if
+ }//end while
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that the opening brace of a function is on the line after the function declaration.
+ *
+ * @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\Standards\Generic\Sniffs\Functions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class OpeningFunctionBraceBsdAllmanSniff implements Sniff
+{
+
+ /**
+ * Should this sniff check function braces?
+ *
+ * @var boolean
+ */
+ public $checkFunctions = true;
+
+ /**
+ * Should this sniff check closure braces?
+ *
+ * @var boolean
+ */
+ public $checkClosures = false;
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return void
+ */
+ public function register()
+ {
+ return array(
+ T_FUNCTION,
+ T_CLOSURE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[$stackPtr]['scope_opener']) === false) {
+ return;
+ }
+
+ if (($tokens[$stackPtr]['code'] === T_FUNCTION
+ && (bool) $this->checkFunctions === false)
+ || ($tokens[$stackPtr]['code'] === T_CLOSURE
+ && (bool) $this->checkClosures === false)
+ ) {
+ return;
+ }
+
+ $openingBrace = $tokens[$stackPtr]['scope_opener'];
+ $closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
+ if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
+ $use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']);
+ if ($use !== false) {
+ $openBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1));
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+ }
+ }
+
+ $functionLine = $tokens[$closeBracket]['line'];
+ $braceLine = $tokens[$openingBrace]['line'];
+
+ $lineDifference = ($braceLine - $functionLine);
+
+ if ($lineDifference === 0) {
+ $error = 'Opening brace should be on a new line';
+ $fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceOnSameLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $indent = $phpcsFile->findFirstOnLine(array(), $openingBrace);
+ if ($tokens[$indent]['code'] === T_WHITESPACE) {
+ $phpcsFile->fixer->addContentBefore($openingBrace, $tokens[$indent]['content']);
+ }
+
+ $phpcsFile->fixer->addNewlineBefore($openingBrace);
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Function opening brace placement', 'same line');
+ } else if ($lineDifference > 1) {
+ $error = 'Opening brace should be on the line after the declaration; found %s blank line(s)';
+ $data = array(($lineDifference - 1));
+ $fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceSpacing', $data);
+ if ($fix === true) {
+ for ($i = ($tokens[$stackPtr]['parenthesis_closer'] + 1); $i < $openingBrace; $i++) {
+ if ($tokens[$i]['line'] === $braceLine) {
+ $phpcsFile->fixer->addNewLineBefore($i);
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+ }
+ }//end if
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($openingBrace + 1), null, true);
+ if ($tokens[$next]['line'] === $tokens[$openingBrace]['line']) {
+ if ($next === $tokens[$stackPtr]['scope_closer']) {
+ // Ignore empty functions.
+ return;
+ }
+
+ $error = 'Opening brace must be the last content on the line';
+ $fix = $phpcsFile->addFixableError($error, $openingBrace, 'ContentAfterBrace');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewline($openingBrace);
+ }
+ }
+
+ // Only continue checking if the opening brace looks good.
+ if ($lineDifference !== 1) {
+ return;
+ }
+
+ // We need to actually find the first piece of content on this line,
+ // as if this is a method with tokens before it (public, static etc)
+ // or an if with an else before it, then we need to start the scope
+ // checking from there, rather than the current token.
+ $lineStart = $phpcsFile->findFirstOnLine(T_WHITESPACE, $stackPtr, true);
+
+ // The opening brace is on the correct line, now it needs to be
+ // checked to be correctly indented.
+ $startColumn = $tokens[$lineStart]['column'];
+ $braceIndent = $tokens[$openingBrace]['column'];
+
+ if ($braceIndent !== $startColumn) {
+ $expected = ($startColumn - 1);
+ $found = ($braceIndent - 1);
+
+ $error = 'Opening brace indented incorrectly; expected %s spaces, found %s';
+ $data = array(
+ $expected,
+ $found,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceIndent', $data);
+ if ($fix === true) {
+ $indent = str_repeat(' ', $expected);
+ if ($found === 0) {
+ $phpcsFile->fixer->addContentBefore($openingBrace, $indent);
+ } else {
+ $phpcsFile->fixer->replaceToken(($openingBrace - 1), $indent);
+ }
+ }
+ }//end if
+
+ $phpcsFile->recordMetric($stackPtr, 'Function opening brace placement', 'new line');
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that the opening brace of a function is on the same line as the function declaration.
+ *
+ * @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\Standards\Generic\Sniffs\Functions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class OpeningFunctionBraceKernighanRitchieSniff implements Sniff
+{
+
+
+ /**
+ * Should this sniff check function braces?
+ *
+ * @var boolean
+ */
+ public $checkFunctions = true;
+
+ /**
+ * Should this sniff check closure braces?
+ *
+ * @var boolean
+ */
+ public $checkClosures = false;
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return void
+ */
+ public function register()
+ {
+ return array(
+ T_FUNCTION,
+ T_CLOSURE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[$stackPtr]['scope_opener']) === false) {
+ return;
+ }
+
+ if (($tokens[$stackPtr]['code'] === T_FUNCTION
+ && (bool) $this->checkFunctions === false)
+ || ($tokens[$stackPtr]['code'] === T_CLOSURE
+ && (bool) $this->checkClosures === false)
+ ) {
+ return;
+ }
+
+ $openingBrace = $tokens[$stackPtr]['scope_opener'];
+ $closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
+ if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
+ $use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']);
+ if ($use !== false) {
+ $openBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1));
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+ }
+ }
+
+ $functionLine = $tokens[$closeBracket]['line'];
+ $braceLine = $tokens[$openingBrace]['line'];
+
+ $lineDifference = ($braceLine - $functionLine);
+
+ if ($lineDifference > 0) {
+ $phpcsFile->recordMetric($stackPtr, 'Function opening brace placement', 'new line');
+ $error = 'Opening brace should be on the same line as the declaration';
+ $fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceOnNewLine');
+ if ($fix === true) {
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($openingBrace - 1), $closeBracket, true);
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->addContent($prev, ' {');
+ $phpcsFile->fixer->replaceToken($openingBrace, '');
+ if ($tokens[($openingBrace + 1)]['code'] === T_WHITESPACE
+ && $tokens[($openingBrace + 2)]['line'] > $tokens[$openingBrace]['line']
+ ) {
+ // Brace is followed by a new line, so remove it to ensure we don't
+ // leave behind a blank line at the top of the block.
+ $phpcsFile->fixer->replaceToken(($openingBrace + 1), '');
+
+ if ($tokens[($openingBrace - 1)]['code'] === T_WHITESPACE
+ && $tokens[($openingBrace - 1)]['line'] === $tokens[$openingBrace]['line']
+ && $tokens[($openingBrace - 2)]['line'] < $tokens[$openingBrace]['line']
+ ) {
+ // Brace is preceeded by indent, so remove it to ensure we don't
+ // leave behind more indent than is required for the first line.
+ $phpcsFile->fixer->replaceToken(($openingBrace - 1), '');
+ }
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+ }//end if
+
+ $phpcsFile->recordMetric($stackPtr, 'Function opening brace placement', 'same line');
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($openingBrace + 1), null, true);
+ if ($tokens[$next]['line'] === $tokens[$openingBrace]['line']) {
+ if ($next === $tokens[$stackPtr]['scope_closer']) {
+ // Ignore empty functions.
+ return;
+ }
+
+ $error = 'Opening brace must be the last content on the line';
+ $fix = $phpcsFile->addFixableError($error, $openingBrace, 'ContentAfterBrace');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewline($openingBrace);
+ }
+ }
+
+ // Only continue checking if the opening brace looks good.
+ if ($lineDifference > 0) {
+ return;
+ }
+
+ // We are looking for tabs, even if they have been replaced, because
+ // we enforce a space here.
+ if (isset($tokens[($openingBrace - 1)]['orig_content']) === true) {
+ $spacing = $tokens[($openingBrace - 1)]['content'];
+ } else {
+ $spacing = $tokens[($openingBrace - 1)]['content'];
+ }
+
+ if ($tokens[($openingBrace - 1)]['code'] !== T_WHITESPACE) {
+ $length = 0;
+ } else if ($spacing === "\t") {
+ $length = '\t';
+ } else {
+ $length = strlen($spacing);
+ }
+
+ if ($length !== 1) {
+ $error = 'Expected 1 space before opening brace; found %s';
+ $data = array($length);
+ $fix = $phpcsFile->addFixableError($error, $closeBracket, 'SpaceBeforeBrace', $data);
+ if ($fix === true) {
+ if ($length === 0 || $length === '\t') {
+ $phpcsFile->fixer->addContentBefore($openingBrace, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($openingBrace - 1), ' ');
+ }
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the cyclomatic complexity (McCabe) for functions.
+ *
+ * The cyclomatic complexity (also called McCabe code metrics)
+ * indicates the complexity within a function by counting
+ * the different paths the function includes.
+ *
+ * @author Johann-Peter Hartmann <hartmann@mayflower.de>
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @copyright 2007-2014 Mayflower GmbH
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Metrics;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class CyclomaticComplexitySniff implements Sniff
+{
+
+ /**
+ * A complexity higher than this value will throw a warning.
+ *
+ * @var integer
+ */
+ public $complexity = 10;
+
+ /**
+ * A complexity higher than this value will throw an error.
+ *
+ * @var integer
+ */
+ public $absoluteComplexity = 20;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_FUNCTION);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $this->currentFile = $phpcsFile;
+
+ $tokens = $phpcsFile->getTokens();
+
+ // Ignore abstract methods.
+ if (isset($tokens[$stackPtr]['scope_opener']) === false) {
+ return;
+ }
+
+ // Detect start and end of this function definition.
+ $start = $tokens[$stackPtr]['scope_opener'];
+ $end = $tokens[$stackPtr]['scope_closer'];
+
+ // Predicate nodes for PHP.
+ $find = array(
+ T_CASE => true,
+ T_DEFAULT => true,
+ T_CATCH => true,
+ T_IF => true,
+ T_FOR => true,
+ T_FOREACH => true,
+ T_WHILE => true,
+ T_DO => true,
+ T_ELSEIF => true,
+ );
+
+ $complexity = 1;
+
+ // Iterate from start to end and count predicate nodes.
+ for ($i = ($start + 1); $i < $end; $i++) {
+ if (isset($find[$tokens[$i]['code']]) === true) {
+ $complexity++;
+ }
+ }
+
+ if ($complexity > $this->absoluteComplexity) {
+ $error = 'Function\'s cyclomatic complexity (%s) exceeds allowed maximum of %s';
+ $data = array(
+ $complexity,
+ $this->absoluteComplexity,
+ );
+ $phpcsFile->addError($error, $stackPtr, 'MaxExceeded', $data);
+ } else if ($complexity > $this->complexity) {
+ $warning = 'Function\'s cyclomatic complexity (%s) exceeds %s; consider refactoring the function';
+ $data = array(
+ $complexity,
+ $this->complexity,
+ );
+ $phpcsFile->addWarning($warning, $stackPtr, 'TooHigh', $data);
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the nesting level for methods.
+ *
+ * @author Johann-Peter Hartmann <hartmann@mayflower.de>
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @copyright 2007-2014 Mayflower GmbH
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Metrics;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class NestingLevelSniff implements Sniff
+{
+
+ /**
+ * A nesting level higher than this value will throw a warning.
+ *
+ * @var integer
+ */
+ public $nestingLevel = 5;
+
+ /**
+ * A nesting level higher than this value will throw an error.
+ *
+ * @var integer
+ */
+ public $absoluteNestingLevel = 10;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_FUNCTION);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // Ignore abstract methods.
+ if (isset($tokens[$stackPtr]['scope_opener']) === false) {
+ return;
+ }
+
+ // Detect start and end of this function definition.
+ $start = $tokens[$stackPtr]['scope_opener'];
+ $end = $tokens[$stackPtr]['scope_closer'];
+
+ $nestingLevel = 0;
+
+ // Find the maximum nesting level of any token in the function.
+ for ($i = ($start + 1); $i < $end; $i++) {
+ $level = $tokens[$i]['level'];
+ if ($nestingLevel < $level) {
+ $nestingLevel = $level;
+ }
+ }
+
+ // We subtract the nesting level of the function itself.
+ $nestingLevel = ($nestingLevel - $tokens[$stackPtr]['level'] - 1);
+
+ if ($nestingLevel > $this->absoluteNestingLevel) {
+ $error = 'Function\'s nesting level (%s) exceeds allowed maximum of %s';
+ $data = array(
+ $nestingLevel,
+ $this->absoluteNestingLevel,
+ );
+ $phpcsFile->addError($error, $stackPtr, 'MaxExceeded', $data);
+ } else if ($nestingLevel > $this->nestingLevel) {
+ $warning = 'Function\'s nesting level (%s) exceeds %s; consider refactoring the function';
+ $data = array(
+ $nestingLevel,
+ $this->nestingLevel,
+ );
+ $phpcsFile->addWarning($warning, $stackPtr, 'TooHigh', $data);
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures method and functions are named correctly.
+ *
+ * @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\Standards\Generic\Sniffs\NamingConventions;
+
+use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
+use PHP_CodeSniffer\Util\Common;
+use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Files\File;
+
+class CamelCapsFunctionNameSniff extends AbstractScopeSniff
+{
+
+ /**
+ * A list of all PHP magic methods.
+ *
+ * @var array
+ */
+ protected $magicMethods = array(
+ 'construct' => true,
+ 'destruct' => true,
+ 'call' => true,
+ 'callstatic' => true,
+ 'get' => true,
+ 'set' => true,
+ 'isset' => true,
+ 'unset' => true,
+ 'sleep' => true,
+ 'wakeup' => true,
+ 'tostring' => true,
+ 'set_state' => true,
+ 'clone' => true,
+ 'invoke' => true,
+ 'debuginfo' => true,
+ );
+
+ /**
+ * A list of all PHP non-magic methods starting with a double underscore.
+ *
+ * These come from PHP modules such as SOAPClient.
+ *
+ * @var array
+ */
+ protected $methodsDoubleUnderscore = array(
+ 'soapcall' => true,
+ 'getlastrequest' => true,
+ 'getlastresponse' => true,
+ 'getlastrequestheaders' => true,
+ 'getlastresponseheaders' => true,
+ 'getfunctions' => true,
+ 'gettypes' => true,
+ 'dorequest' => true,
+ 'setcookie' => true,
+ 'setlocation' => true,
+ 'setsoapheaders' => true,
+ );
+
+ /**
+ * A list of all PHP magic functions.
+ *
+ * @var array
+ */
+ protected $magicFunctions = array('autoload' => true);
+
+ /**
+ * If TRUE, the string must not have two capital letters next to each other.
+ *
+ * @var boolean
+ */
+ public $strict = true;
+
+
+ /**
+ * Constructs a Generic_Sniffs_NamingConventions_CamelCapsFunctionNameSniff.
+ */
+ public function __construct()
+ {
+ parent::__construct(Tokens::$ooScopeTokens, array(T_FUNCTION), true);
+
+ }//end __construct()
+
+
+ /**
+ * Processes the tokens within the scope.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed.
+ * @param int $stackPtr The position where this token was
+ * found.
+ * @param int $currScope The position of the current scope.
+ *
+ * @return void
+ */
+ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
+ {
+ $methodName = $phpcsFile->getDeclarationName($stackPtr);
+ if ($methodName === null) {
+ // Ignore closures.
+ return;
+ }
+
+ $className = $phpcsFile->getDeclarationName($currScope);
+ $errorData = array($className.'::'.$methodName);
+
+ // Is this a magic method. i.e., is prefixed with "__" ?
+ if (preg_match('|^__[^_]|', $methodName) !== 0) {
+ $magicPart = strtolower(substr($methodName, 2));
+ if (isset($this->magicMethods[$magicPart]) === false
+ && isset($this->methodsDoubleUnderscore[$magicPart]) === false
+ ) {
+ $error = 'Method name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore';
+ $phpcsFile->addError($error, $stackPtr, 'MethodDoubleUnderscore', $errorData);
+ }
+
+ return;
+ }
+
+ // PHP4 constructors are allowed to break our rules.
+ if ($methodName === $className) {
+ return;
+ }
+
+ // PHP4 destructors are allowed to break our rules.
+ if ($methodName === '_'.$className) {
+ return;
+ }
+
+ // Ignore first underscore in methods prefixed with "_".
+ $methodName = ltrim($methodName, '_');
+
+ $methodProps = $phpcsFile->getMethodProperties($stackPtr);
+ if (Common::isCamelCaps($methodName, false, true, $this->strict) === false) {
+ if ($methodProps['scope_specified'] === true) {
+ $error = '%s method name "%s" is not in camel caps format';
+ $data = array(
+ ucfirst($methodProps['scope']),
+ $errorData[0],
+ );
+ $phpcsFile->addError($error, $stackPtr, 'ScopeNotCamelCaps', $data);
+ } else {
+ $error = 'Method name "%s" is not in camel caps format';
+ $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData);
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'no');
+ return;
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'yes');
+ }
+
+ }//end processTokenWithinScope()
+
+
+ /**
+ * Processes the tokens outside the scope.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed.
+ * @param int $stackPtr The position where this token was
+ * found.
+ *
+ * @return void
+ */
+ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
+ {
+ $functionName = $phpcsFile->getDeclarationName($stackPtr);
+ if ($functionName === null) {
+ // Ignore closures.
+ return;
+ }
+
+ $errorData = array($functionName);
+
+ // Is this a magic function. i.e., it is prefixed with "__".
+ if (preg_match('|^__[^_]|', $functionName) !== 0) {
+ $magicPart = strtolower(substr($functionName, 2));
+ if (isset($this->magicFunctions[$magicPart]) === false) {
+ $error = 'Function name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore';
+ $phpcsFile->addError($error, $stackPtr, 'FunctionDoubleUnderscore', $errorData);
+ }
+
+ return;
+ }
+
+ // Ignore first underscore in functions prefixed with "_".
+ $functionName = ltrim($functionName, '_');
+
+ if (Common::isCamelCaps($functionName, false, true, $this->strict) === false) {
+ $error = 'Function name "%s" is not in camel caps format';
+ $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData);
+ $phpcsFile->recordMetric($stackPtr, 'CamelCase function name', 'no');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'yes');
+ }
+
+ }//end processTokenOutsideScope()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Bans PHP 4 style constructors.
+ *
+ * Favor PHP 5 constructor syntax, which uses "function __construct()".
+ * Avoid PHP 4 constructor syntax, which uses "function ClassName()".
+ *
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @author Leif Wickland <lwickland@rightnow.com>
+ * @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\Standards\Generic\Sniffs\NamingConventions;
+
+use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
+use PHP_CodeSniffer\Files\File;
+
+class ConstructorNameSniff extends AbstractScopeSniff
+{
+
+ /**
+ * The name of the class we are currently checking.
+ *
+ * @var string
+ */
+ private $currentClass = '';
+
+ /**
+ * A list of functions in the current class.
+ *
+ * @var string[]
+ */
+ private $functionList = array();
+
+
+ /**
+ * Constructs the test with the tokens it wishes to listen for.
+ */
+ public function __construct()
+ {
+ parent::__construct(array(T_CLASS, T_ANON_CLASS, T_INTERFACE), array(T_FUNCTION), true);
+
+ }//end __construct()
+
+
+ /**
+ * Processes this test when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $currScope A pointer to the start of the scope.
+ *
+ * @return void
+ */
+ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
+ {
+ $className = $phpcsFile->getDeclarationName($currScope);
+ if ($className !== $this->currentClass) {
+ $this->loadFunctionNamesInScope($phpcsFile, $currScope);
+ $this->currentClass = $className;
+ }
+
+ $methodName = $phpcsFile->getDeclarationName($stackPtr);
+
+ if (strcasecmp($methodName, $className) === 0) {
+ if (in_array('__construct', $this->functionList) === false) {
+ $error = 'PHP4 style constructors are not allowed; use "__construct()" instead';
+ $phpcsFile->addError($error, $stackPtr, 'OldStyle');
+ }
+ } else if (strcasecmp($methodName, '__construct') !== 0) {
+ // Not a constructor.
+ return;
+ }
+
+ $tokens = $phpcsFile->getTokens();
+
+ $parentClassName = $phpcsFile->findExtendedClassName($currScope);
+ if ($parentClassName === false) {
+ return;
+ }
+
+ // Stop if the constructor doesn't have a body, like when it is abstract.
+ if (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ return;
+ }
+
+ $endFunctionIndex = $tokens[$stackPtr]['scope_closer'];
+ $startIndex = $stackPtr;
+ while (($doubleColonIndex = $phpcsFile->findNext(T_DOUBLE_COLON, $startIndex, $endFunctionIndex)) !== false) {
+ if ($tokens[($doubleColonIndex + 1)]['code'] === T_STRING
+ && $tokens[($doubleColonIndex + 1)]['content'] === $parentClassName
+ ) {
+ $error = 'PHP4 style calls to parent constructors are not allowed; use "parent::__construct()" instead';
+ $phpcsFile->addError($error, ($doubleColonIndex + 1), 'OldStyleCall');
+ }
+
+ $startIndex = ($doubleColonIndex + 1);
+ }
+
+ }//end processTokenWithinScope()
+
+
+ /**
+ * 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.
+ *
+ * @return void
+ */
+ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
+ {
+
+ }//end processTokenOutsideScope()
+
+
+ /**
+ * Extracts all the function names found in the given scope.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being scanned.
+ * @param int $currScope A pointer to the start of the scope.
+ *
+ * @return void
+ */
+ protected function loadFunctionNamesInScope(File $phpcsFile, $currScope)
+ {
+ $this->functionList = array();
+ $tokens = $phpcsFile->getTokens();
+
+ for ($i = ($tokens[$currScope]['scope_opener'] + 1); $i < $tokens[$currScope]['scope_closer']; $i++) {
+ if ($tokens[$i]['code'] !== T_FUNCTION) {
+ continue;
+ }
+
+ $next = $phpcsFile->findNext(T_STRING, $i);
+ $this->functionList[] = trim($tokens[$next]['content']);
+ }
+
+ }//end loadFunctionNamesInScope()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that constant names are all uppercase.
+ *
+ * @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\Standards\Generic\Sniffs\NamingConventions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class UpperCaseConstantNameSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_STRING);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $constName = $tokens[$stackPtr]['content'];
+
+ // If this token is in a heredoc, ignore it.
+ if ($phpcsFile->hasCondition($stackPtr, T_START_HEREDOC) === true) {
+ return;
+ }
+
+ // Special case for PHP 5.5 class name resolution.
+ if (strtolower($constName) === 'class'
+ && $tokens[($stackPtr - 1)]['code'] === T_DOUBLE_COLON
+ ) {
+ return;
+ }
+
+ // Special case for PHPUnit.
+ if ($constName === 'PHPUnit_MAIN_METHOD') {
+ return;
+ }
+
+ // If the next non-whitespace token after this token
+ // is not an opening parenthesis then it is not a function call.
+ for ($openBracket = ($stackPtr + 1); $openBracket < $phpcsFile->numTokens; $openBracket++) {
+ if ($tokens[$openBracket]['code'] !== T_WHITESPACE) {
+ break;
+ }
+ }
+
+ if ($openBracket === $phpcsFile->numTokens) {
+ return;
+ }
+
+ if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
+ $functionKeyword = $phpcsFile->findPrevious(
+ array(
+ T_WHITESPACE,
+ T_COMMA,
+ T_COMMENT,
+ T_STRING,
+ T_NS_SEPARATOR,
+ ),
+ ($stackPtr - 1),
+ null,
+ true
+ );
+
+ if ($tokens[$functionKeyword]['code'] !== T_CONST) {
+ return;
+ }
+
+ // This is a class constant.
+ if (strtoupper($constName) !== $constName) {
+ if (strtolower($constName) === $constName) {
+ $phpcsFile->recordMetric($stackPtr, 'Constant name case', 'lower');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Constant name case', 'mixed');
+ }
+
+ $error = 'Class constants must be uppercase; expected %s but found %s';
+ $data = array(
+ strtoupper($constName),
+ $constName,
+ );
+ $phpcsFile->addError($error, $stackPtr, 'ClassConstantNotUpperCase', $data);
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Constant name case', 'upper');
+ }
+
+ return;
+ }//end if
+
+ if (strtolower($constName) !== 'define') {
+ return;
+ }
+
+ /*
+ This may be a "define" function call.
+ */
+
+ // Make sure this is not a method call.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] === T_OBJECT_OPERATOR
+ || $tokens[$prev]['code'] === T_DOUBLE_COLON
+ ) {
+ return;
+ }
+
+ // The next non-whitespace token must be the constant name.
+ $constPtr = $phpcsFile->findNext(T_WHITESPACE, ($openBracket + 1), null, true);
+ if ($tokens[$constPtr]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
+ return;
+ }
+
+ $constName = $tokens[$constPtr]['content'];
+
+ // Check for constants like self::CONSTANT.
+ $prefix = '';
+ $splitPos = strpos($constName, '::');
+ if ($splitPos !== false) {
+ $prefix = substr($constName, 0, ($splitPos + 2));
+ $constName = substr($constName, ($splitPos + 2));
+ }
+
+ // Strip namesspace from constant like /foo/bar/CONSTANT.
+ $splitPos = strrpos($constName, '\\');
+ if ($splitPos !== false) {
+ $prefix = substr($constName, 0, ($splitPos + 1));
+ $constName = substr($constName, ($splitPos + 1));
+ }
+
+ if (strtoupper($constName) !== $constName) {
+ if (strtolower($constName) === $constName) {
+ $phpcsFile->recordMetric($stackPtr, 'Constant name case', 'lower');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Constant name case', 'mixed');
+ }
+
+ $error = 'Constants must be uppercase; expected %s but found %s';
+ $data = array(
+ $prefix.strtoupper($constName),
+ $prefix.$constName,
+ );
+ $phpcsFile->addError($error, $stackPtr, 'ConstantNotUpperCase', $data);
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Constant name case', 'upper');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Bans the use of the backtick execution operator.
+ *
+ * @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\Standards\Generic\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class BacktickOperatorSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_BACKTICK);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $error = 'Use of the backtick operator is forbidden';
+ $phpcsFile->addError($error, $stackPtr, 'Found');
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that the opening PHP tag is the first content in a file.
+ *
+ * @author Andy Grunwald <andygrunwald@gmail.com>
+ * @copyright 2010-2014 Andy Grunwald
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class CharacterBeforePHPOpeningTagSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $expected = 0;
+ if ($stackPtr > 0) {
+ // Allow a shebang line.
+ $tokens = $phpcsFile->getTokens();
+ if (substr($tokens[0]['content'], 0, 2) === '#!') {
+ $expected = 1;
+ }
+ }
+
+ if ($stackPtr !== $expected) {
+ $error = 'The opening PHP tag must be the first content in the file';
+ $phpcsFile->addError($error, $stackPtr, 'Found');
+ }
+
+ // Skip the rest of the file so we don't pick up additional
+ // open tags, typically embedded in HTML.
+ return $phpcsFile->numTokens;
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that open PHP tags are paired with closing tags.
+ *
+ * @author Stefano Kowalke <blueduck@gmx.net>
+ * @copyright 2010-2014 Stefano Kowalke
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ClosingPHPTagSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $closeTag = $phpcsFile->findNext(T_CLOSE_TAG, $stackPtr);
+ if ($closeTag === false) {
+ $error = 'The PHP open tag does not have a corresponding PHP close tag';
+ $phpcsFile->addError($error, $stackPtr, 'NotFound');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Discourages the use of deprecated PHP functions.
+ *
+ * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
+ * @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\Standards\Generic\Sniffs\PHP;
+
+class DeprecatedFunctionsSniff extends ForbiddenFunctionsSniff
+{
+
+ /**
+ * A list of forbidden functions with their alternatives.
+ *
+ * The value is NULL if no alternative exists. IE, the
+ * function should just not be used.
+ *
+ * @var array(string => string|null)
+ */
+ public $forbiddenFunctions = array();
+
+
+ /**
+ * Constructor.
+ *
+ * Uses the Reflection API to get a list of deprecated functions.
+ */
+ public function __construct()
+ {
+ $functions = get_defined_functions();
+
+ foreach ($functions['internal'] as $functionName) {
+ $function = new \ReflectionFunction($functionName);
+ if (method_exists($function, 'isDeprecated') === false) {
+ break;
+ }
+
+ if ($function->isDeprecated() === true) {
+ $this->forbiddenFunctions[$functionName] = null;
+ }
+ }
+
+ }//end __construct()
+
+
+ /**
+ * Generates the error or warning for this sniff.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the forbidden function
+ * in the token array.
+ * @param string $function The name of the forbidden function.
+ * @param string $pattern The pattern used for the match.
+ *
+ * @return void
+ */
+ protected function addError($phpcsFile, $stackPtr, $function, $pattern=null)
+ {
+ $data = array($function);
+ $error = 'Function %s() has been deprecated';
+ $type = 'Deprecated';
+
+ if ($this->error === true) {
+ $phpcsFile->addError($error, $stackPtr, $type, $data);
+ } else {
+ $phpcsFile->addWarning($error, $stackPtr, $type, $data);
+ }
+
+ }//end addError()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that no alternative PHP tags are used.
+ *
+ * If alternative PHP open tags are found, this sniff can fix both the open and close tags.
+ *
+ * @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\Standards\Generic\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Config;
+
+class DisallowAlternativePHPTagsSniff implements Sniff
+{
+
+ /**
+ * Whether ASP tags are enabled or not.
+ *
+ * @var boolean
+ */
+ private $aspTags = false;
+
+ /**
+ * The current PHP version.
+ *
+ * @var integer
+ */
+ private $phpVersion = null;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ if ($this->phpVersion === null) {
+ $this->phpVersion = Config::getConfigData('php_version');
+ if ($this->phpVersion === null) {
+ $this->phpVersion = PHP_VERSION_ID;
+ }
+ }
+
+ if ($this->phpVersion < 70000) {
+ $this->aspTags = (boolean) ini_get('asp_tags');
+ }
+
+ return array(
+ T_OPEN_TAG,
+ T_OPEN_TAG_WITH_ECHO,
+ T_INLINE_HTML,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $openTag = $tokens[$stackPtr];
+ $content = $openTag['content'];
+
+ if (trim($content) === '') {
+ return;
+ }
+
+ if ($openTag['code'] === T_OPEN_TAG) {
+ if ($content === '<%') {
+ $error = 'ASP style opening tag used; expected "<?php" but found "%s"';
+ $closer = $this->findClosingTag($phpcsFile, $tokens, $stackPtr, '%>');
+ $errorCode = 'ASPOpenTagFound';
+ } else if (strpos($content, '<script ') !== false) {
+ $error = 'Script style opening tag used; expected "<?php" but found "%s"';
+ $closer = $this->findClosingTag($phpcsFile, $tokens, $stackPtr, '</script>');
+ $errorCode = 'ScriptOpenTagFound';
+ }
+
+ if (isset($error, $closer, $errorCode) === true) {
+ $data = array($content);
+
+ if ($closer === false) {
+ $phpcsFile->addError($error, $stackPtr, $errorCode, $data);
+ } else {
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, $errorCode, $data);
+ if ($fix === true) {
+ $this->addChangeset($phpcsFile, $tokens, $stackPtr, $closer);
+ }
+ }
+ }
+
+ return;
+ }//end if
+
+ if ($openTag['code'] === T_OPEN_TAG_WITH_ECHO && $content === '<%=') {
+ $error = 'ASP style opening tag used with echo; expected "<?php echo %s ..." but found "%s %s ..."';
+ $nextVar = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ $snippet = $this->getSnippet($tokens[$nextVar]['content']);
+ $data = array(
+ $snippet,
+ $content,
+ $snippet,
+ );
+
+ $closer = $this->findClosingTag($phpcsFile, $tokens, $stackPtr, '%>');
+
+ if ($closer === false) {
+ $phpcsFile->addError($error, $stackPtr, 'ASPShortOpenTagFound', $data);
+ } else {
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'ASPShortOpenTagFound', $data);
+ if ($fix === true) {
+ $this->addChangeset($phpcsFile, $tokens, $stackPtr, $closer, true);
+ }
+ }
+
+ return;
+ }//end if
+
+ // Account for incorrect script open tags.
+ // The "(?:<s)?" in the regex is to work-around a bug in PHP 5.2.
+ if ($openTag['code'] === T_INLINE_HTML
+ && preg_match('`((?:<s)?cript (?:[^>]+)?language=[\'"]?php[\'"]?(?:[^>]+)?>)`i', $content, $match) === 1
+ ) {
+ $error = 'Script style opening tag used; expected "<?php" but found "%s"';
+ $snippet = $this->getSnippet($content, $match[1]);
+ $data = array($match[1].$snippet);
+
+ $phpcsFile->addError($error, $stackPtr, 'ScriptOpenTagFound', $data);
+ return;
+ }
+
+ if ($openTag['code'] === T_INLINE_HTML && $this->aspTags === false) {
+ if (strpos($content, '<%=') !== false) {
+ $error = 'Possible use of ASP style short opening tags detected; found: %s';
+ $snippet = $this->getSnippet($content, '<%=');
+ $data = array('<%='.$snippet);
+
+ $phpcsFile->addWarning($error, $stackPtr, 'MaybeASPShortOpenTagFound', $data);
+ } else if (strpos($content, '<%') !== false) {
+ $error = 'Possible use of ASP style opening tags detected; found: %s';
+ $snippet = $this->getSnippet($content, '<%');
+ $data = array('<%'.$snippet);
+
+ $phpcsFile->addWarning($error, $stackPtr, 'MaybeASPOpenTagFound', $data);
+ }
+ }
+
+ }//end process()
+
+
+ /**
+ * Get a snippet from a HTML token.
+ *
+ * @param string $content The content of the HTML token.
+ * @param string $start Partial string to use as a starting point for the snippet.
+ * @param int $length The target length of the snippet to get. Defaults to 40.
+ *
+ * @return string
+ */
+ protected function getSnippet($content, $start='', $length=40)
+ {
+ $startPos = 0;
+
+ if ($start !== '') {
+ $startPos = strpos($content, $start);
+ if ($startPos !== false) {
+ $startPos += strlen($start);
+ }
+ }
+
+ $snippet = substr($content, $startPos, $length);
+ if ((strlen($content) - $startPos) > $length) {
+ $snippet .= '...';
+ }
+
+ return $snippet;
+
+ }//end getSnippet()
+
+
+ /**
+ * Try and find a matching PHP closing tag.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $tokens The token stack.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param string $content The expected content of the closing tag to match the opener.
+ *
+ * @return int|false Pointer to the position in the stack for the closing tag or false if not found.
+ */
+ protected function findClosingTag(File $phpcsFile, $tokens, $stackPtr, $content)
+ {
+ $closer = $phpcsFile->findNext(T_CLOSE_TAG, ($stackPtr + 1));
+
+ if ($closer !== false && $content === trim($tokens[$closer]['content'])) {
+ return $closer;
+ }
+
+ return false;
+
+ }//end findClosingTag()
+
+
+ /**
+ * Add a changeset to replace the alternative PHP tags.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $tokens The token stack.
+ * @param int $openTagPointer Stack pointer to the PHP open tag.
+ * @param int $closeTagPointer Stack pointer to the PHP close tag.
+ * @param bool $echo Whether to add 'echo' or not.
+ *
+ * @return void
+ */
+ protected function addChangeset(File $phpcsFile, $tokens, $openTagPointer, $closeTagPointer, $echo=false)
+ {
+ // Build up the open tag replacement and make sure there's always whitespace behind it.
+ $openReplacement = '<?php';
+ if ($echo === true) {
+ $openReplacement .= ' echo';
+ }
+
+ if ($tokens[($openTagPointer + 1)]['code'] !== T_WHITESPACE) {
+ $openReplacement .= ' ';
+ }
+
+ // Make sure we don't remove any line breaks after the closing tag.
+ $regex = '`'.preg_quote(trim($tokens[$closeTagPointer]['content'])).'`';
+ $closeReplacement = preg_replace($regex, '?>', $tokens[$closeTagPointer]['content']);
+
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken($openTagPointer, $openReplacement);
+ $phpcsFile->fixer->replaceToken($closeTagPointer, $closeReplacement);
+ $phpcsFile->fixer->endChangeset();
+
+ }//end addChangeset()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Makes sure that shorthand PHP open tags are not used.
+ *
+ * @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\Standards\Generic\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class DisallowShortOpenTagSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ $targets = array(
+ T_OPEN_TAG,
+ T_OPEN_TAG_WITH_ECHO,
+ );
+
+ $shortOpenTags = (boolean) ini_get('short_open_tag');
+ if ($shortOpenTags === false) {
+ $targets[] = T_INLINE_HTML;
+ }
+
+ return $targets;
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $token = $tokens[$stackPtr];
+
+ if ($token['code'] === T_OPEN_TAG && $token['content'] === '<?') {
+ $error = 'Short PHP opening tag used; expected "<?php" but found "%s"';
+ $data = array($token['content']);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found', $data);
+ if ($fix === true) {
+ $correctOpening = '<?php';
+ if (isset($tokens[($stackPtr + 1)]) === true && $tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
+ // Avoid creation of invalid open tags like <?phpecho if the original was <?echo .
+ $correctOpening .= ' ';
+ }
+
+ $phpcsFile->fixer->replaceToken($stackPtr, $correctOpening);
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'PHP short open tag used', 'yes');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'PHP short open tag used', 'no');
+ }
+
+ if ($token['code'] === T_OPEN_TAG_WITH_ECHO) {
+ $nextVar = $tokens[$phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true)];
+ $error = 'Short PHP opening tag used with echo; expected "<?php echo %s ..." but found "%s %s ..."';
+ $data = array(
+ $nextVar['content'],
+ $token['content'],
+ $nextVar['content'],
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'EchoFound', $data);
+ if ($fix === true) {
+ if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
+ $phpcsFile->fixer->replaceToken($stackPtr, '<?php echo ');
+ } else {
+ $phpcsFile->fixer->replaceToken($stackPtr, '<?php echo');
+ }
+ }
+ }
+
+ if ($token['code'] === T_INLINE_HTML) {
+ $content = $token['content'];
+ $openerFound = strpos($content, '<?');
+
+ if ($openerFound === false) {
+ return;
+ }
+
+ $closerFound = false;
+
+ // Inspect current token and subsequent inline HTML token to find a close tag.
+ for ($i = $stackPtr; $i < $phpcsFile->numTokens; $i++) {
+ if ($tokens[$i]['code'] !== T_INLINE_HTML) {
+ break;
+ }
+
+ $closerFound = strrpos($tokens[$i]['content'], '?>');
+ if ($closerFound !== false) {
+ if ($i !== $stackPtr) {
+ break;
+ } else if ($closerFound > $openerFound) {
+ break;
+ } else {
+ $closerFound = false;
+ }
+ }
+ }
+
+ if ($closerFound !== false) {
+ $error = 'Possible use of short open tags detected; found: %s';
+ $snippet = $this->getSnippet($content, '<?');
+ $data = array('<?'.$snippet);
+
+ $phpcsFile->addWarning($error, $stackPtr, 'PossibleFound', $data);
+
+ // Skip forward to the token containing the closer.
+ if (($i - 1) > $stackPtr) {
+ return $i;
+ }
+ }
+ }//end if
+
+ }//end process()
+
+
+ /**
+ * Get a snippet from a HTML token.
+ *
+ * @param string $content The content of the HTML token.
+ * @param string $start Partial string to use as a starting point for the snippet.
+ * @param int $length The target length of the snippet to get. Defaults to 40.
+ *
+ * @return string
+ */
+ protected function getSnippet($content, $start='', $length=40)
+ {
+ $startPos = 0;
+
+ if ($start !== '') {
+ $startPos = strpos($content, $start);
+ if ($startPos !== false) {
+ $startPos += strlen($start);
+ }
+ }
+
+ $snippet = substr($content, $startPos, $length);
+ if ((strlen($content) - $startPos) > $length) {
+ $snippet .= '...';
+ }
+
+ return $snippet;
+
+ }//end getSnippet()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Discourages the use of alias functions.
+ *
+ * Alias functions are kept in PHP for compatibility
+ * with older versions. Can be used to forbid the use of any function.
+ *
+ * @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\Standards\Generic\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ForbiddenFunctionsSniff implements Sniff
+{
+
+ /**
+ * A list of forbidden functions with their alternatives.
+ *
+ * The value is NULL if no alternative exists. IE, the
+ * function should just not be used.
+ *
+ * @var array<string, string|null>
+ */
+ public $forbiddenFunctions = array(
+ 'sizeof' => 'count',
+ 'delete' => 'unset',
+ );
+
+ /**
+ * A cache of forbidden function names, for faster lookups.
+ *
+ * @var string[]
+ */
+ protected $forbiddenFunctionNames = array();
+
+ /**
+ * If true, forbidden functions will be considered regular expressions.
+ *
+ * @var boolean
+ */
+ protected $patternMatch = false;
+
+ /**
+ * If true, an error will be thrown; otherwise a warning.
+ *
+ * @var boolean
+ */
+ public $error = true;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ // Everyone has had a chance to figure out what forbidden functions
+ // they want to check for, so now we can cache out the list.
+ $this->forbiddenFunctionNames = array_keys($this->forbiddenFunctions);
+
+ if ($this->patternMatch === true) {
+ foreach ($this->forbiddenFunctionNames as $i => $name) {
+ $this->forbiddenFunctionNames[$i] = '/'.$name.'/i';
+ }
+
+ return array(T_STRING);
+ }
+
+ // If we are not pattern matching, we need to work out what
+ // tokens to listen for.
+ $string = '<?php ';
+ foreach ($this->forbiddenFunctionNames as $name) {
+ $string .= $name.'();';
+ }
+
+ $register = array();
+
+ $tokens = token_get_all($string);
+ array_shift($tokens);
+ foreach ($tokens as $token) {
+ if (is_array($token) === true) {
+ $register[] = $token[0];
+ }
+ }
+
+ $this->forbiddenFunctionNames = array_map('strtolower', $this->forbiddenFunctionNames);
+ $this->forbiddenFunctions = array_combine($this->forbiddenFunctionNames, $this->forbiddenFunctions);
+
+ return array_unique($register);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $ignore = array(
+ T_DOUBLE_COLON => true,
+ T_OBJECT_OPERATOR => true,
+ T_FUNCTION => true,
+ T_CONST => true,
+ T_PUBLIC => true,
+ T_PRIVATE => true,
+ T_PROTECTED => true,
+ T_AS => true,
+ T_NEW => true,
+ T_INSTEADOF => true,
+ T_NS_SEPARATOR => true,
+ T_IMPLEMENTS => true,
+ );
+
+ $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+
+ // If function call is directly preceded by a NS_SEPARATOR it points to the
+ // global namespace, so we should still catch it.
+ if ($tokens[$prevToken]['code'] === T_NS_SEPARATOR) {
+ $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, ($prevToken - 1), null, true);
+ if ($tokens[$prevToken]['code'] === T_STRING) {
+ // Not in the global namespace.
+ return;
+ }
+ }
+
+ if (isset($ignore[$tokens[$prevToken]['code']]) === true) {
+ // Not a call to a PHP function.
+ return;
+ }
+
+ $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if (isset($ignore[$tokens[$nextToken]['code']]) === true) {
+ // Not a call to a PHP function.
+ return;
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_STRING && $tokens[$nextToken]['code'] !== T_OPEN_PARENTHESIS) {
+ // Not a call to a PHP function.
+ return;
+ }
+
+ $function = strtolower($tokens[$stackPtr]['content']);
+ $pattern = null;
+
+ if ($this->patternMatch === true) {
+ $count = 0;
+ $pattern = preg_replace(
+ $this->forbiddenFunctionNames,
+ $this->forbiddenFunctionNames,
+ $function,
+ 1,
+ $count
+ );
+
+ if ($count === 0) {
+ return;
+ }
+
+ // Remove the pattern delimiters and modifier.
+ $pattern = substr($pattern, 1, -2);
+ } else {
+ if (in_array($function, $this->forbiddenFunctionNames) === false) {
+ return;
+ }
+ }//end if
+
+ $this->addError($phpcsFile, $stackPtr, $tokens[$stackPtr]['content'], $pattern);
+
+ }//end process()
+
+
+ /**
+ * Generates the error or warning for this sniff.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the forbidden function
+ * in the token array.
+ * @param string $function The name of the forbidden function.
+ * @param string $pattern The pattern used for the match.
+ *
+ * @return void
+ */
+ protected function addError($phpcsFile, $stackPtr, $function, $pattern=null)
+ {
+ $data = array($function);
+ $error = 'The use of function %s() is ';
+ if ($this->error === true) {
+ $type = 'Found';
+ $error .= 'forbidden';
+ } else {
+ $type = 'Discouraged';
+ $error .= 'discouraged';
+ }
+
+ if ($pattern === null) {
+ $pattern = strtolower($function);
+ }
+
+ if ($this->forbiddenFunctions[$pattern] !== null
+ && $this->forbiddenFunctions[$pattern] !== 'null'
+ ) {
+ $type .= 'WithAlternative';
+ $data[] = $this->forbiddenFunctions[$pattern];
+ $error .= '; use %s() instead';
+ }
+
+ if ($this->error === true) {
+ $phpcsFile->addError($error, $stackPtr, $type, $data);
+ } else {
+ $phpcsFile->addWarning($error, $stackPtr, $type, $data);
+ }
+
+ }//end addError()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that all uses of true, false and null are lowercase.
+ *
+ * @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\Standards\Generic\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class LowerCaseConstantSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_TRUE,
+ T_FALSE,
+ T_NULL,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $keyword = $tokens[$stackPtr]['content'];
+ $expected = strtolower($keyword);
+ if ($keyword !== $expected) {
+ if ($keyword === strtoupper($keyword)) {
+ $phpcsFile->recordMetric($stackPtr, 'PHP constant case', 'upper');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'PHP constant case', 'mixed');
+ }
+
+ $error = 'TRUE, FALSE and NULL must be lowercase; expected "%s" but found "%s"';
+ $data = array(
+ $expected,
+ $keyword,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, $expected);
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'PHP constant case', 'lower');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that all PHP keywords are lowercase.
+ *
+ * @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\Standards\Generic\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class LowerCaseKeywordSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_HALT_COMPILER,
+ T_ABSTRACT,
+ T_ARRAY,
+ T_ARRAY_HINT,
+ T_AS,
+ T_BREAK,
+ T_CALLABLE,
+ T_CASE,
+ T_CATCH,
+ T_CLASS,
+ T_CLONE,
+ T_CLOSURE,
+ T_CONST,
+ T_CONTINUE,
+ T_DECLARE,
+ T_DEFAULT,
+ T_DO,
+ T_ECHO,
+ T_ELSE,
+ T_ELSEIF,
+ T_EMPTY,
+ T_ENDDECLARE,
+ T_ENDFOR,
+ T_ENDFOREACH,
+ T_ENDIF,
+ T_ENDSWITCH,
+ T_ENDWHILE,
+ T_EVAL,
+ T_EXIT,
+ T_EXTENDS,
+ T_FINAL,
+ T_FINALLY,
+ T_FOR,
+ T_FOREACH,
+ T_FUNCTION,
+ T_GLOBAL,
+ T_GOTO,
+ T_IF,
+ T_IMPLEMENTS,
+ T_INCLUDE,
+ T_INCLUDE_ONCE,
+ T_INSTANCEOF,
+ T_INSTEADOF,
+ T_INTERFACE,
+ T_ISSET,
+ T_LIST,
+ T_LOGICAL_AND,
+ T_LOGICAL_OR,
+ T_LOGICAL_XOR,
+ T_NAMESPACE,
+ T_NEW,
+ T_PARENT,
+ T_PRINT,
+ T_PRIVATE,
+ T_PROTECTED,
+ T_PUBLIC,
+ T_REQUIRE,
+ T_REQUIRE_ONCE,
+ T_RETURN,
+ T_SELF,
+ T_STATIC,
+ T_SWITCH,
+ T_THROW,
+ T_TRAIT,
+ T_TRY,
+ T_UNSET,
+ T_USE,
+ T_VAR,
+ T_WHILE,
+ T_YIELD,
+ T_YIELD_FROM,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $keyword = $tokens[$stackPtr]['content'];
+ if (strtolower($keyword) !== $keyword) {
+ if ($keyword === strtoupper($keyword)) {
+ $phpcsFile->recordMetric($stackPtr, 'PHP keyword case', 'upper');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'PHP keyword case', 'mixed');
+ }
+
+ $error = 'PHP keywords must be lowercase; expected "%s" but found "%s"';
+ $data = array(
+ strtolower($keyword),
+ $keyword,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, strtolower($keyword));
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'PHP keyword case', 'lower');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Throws an error or warning when any code prefixed with an asperand is encountered.
+ *
+ * <code>
+ * if (@in_array($array, $needle))
+ * {
+ * doSomething();
+ * }
+ * </code>
+ *
+ * @author Andy Brockhurst <abrock@yahoo-inc.com>
+ * @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\Standards\Generic\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class NoSilencedErrorsSniff implements Sniff
+{
+
+ /**
+ * If true, an error will be thrown; otherwise a warning.
+ *
+ * @var boolean
+ */
+ public $error = false;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_ASPERAND);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $error = 'Silencing errors is forbidden';
+ if ($this->error === true) {
+ $error = 'Silencing errors is forbidden';
+ $phpcsFile->addError($error, $stackPtr, 'Forbidden');
+ } else {
+ $error = 'Silencing errors is discouraged';
+ $phpcsFile->addWarning($error, $stackPtr, 'Discouraged');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures the PHP_SAPI constant is used instead of php_sapi_name().
+ *
+ * @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\Standards\Generic\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class SAPIUsageSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_STRING);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $ignore = array(
+ T_DOUBLE_COLON => true,
+ T_OBJECT_OPERATOR => true,
+ T_FUNCTION => true,
+ T_CONST => true,
+ );
+
+ $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if (isset($ignore[$tokens[$prevToken]['code']]) === true) {
+ // Not a call to a PHP function.
+ return;
+ }
+
+ $function = strtolower($tokens[$stackPtr]['content']);
+ if ($function === 'php_sapi_name') {
+ $error = 'Use the PHP_SAPI constant instead of calling php_sapi_name()';
+ $phpcsFile->addError($error, $stackPtr, 'FunctionFound');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures PHP believes the syntax is clean.
+ *
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @author Blaine Schmeisser <blainesch@gmail.com>
+ * @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\Standards\Generic\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Config;
+
+class SyntaxSniff implements Sniff
+{
+
+ /**
+ * The path to the PHP version we are checking with.
+ *
+ * @var string
+ */
+ private $phpPath = null;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ if ($this->phpPath === null) {
+ $this->phpPath = Config::getExecutablePath('php');
+ if ($this->phpPath === null) {
+ // PHP_BINARY is available in PHP 5.4+.
+ if (defined('PHP_BINARY') === true) {
+ $this->phpPath = PHP_BINARY;
+ } else {
+ return;
+ }
+ }
+ }
+
+ $fileName = escapeshellarg($phpcsFile->getFilename());
+ if (defined('HHVM_VERSION') === false) {
+ $cmd = escapeshellcmd($this->phpPath)." -l -d error_prepend_string='' $fileName 2>&1";
+ } else {
+ $cmd = escapeshellcmd($this->phpPath)." -l $fileName 2>&1";
+ }
+
+ $output = shell_exec($cmd);
+ $matches = array();
+ if (preg_match('/^.*error:(.*) in .* on line ([0-9]+)/m', trim($output), $matches) === 1) {
+ $error = trim($matches[1]);
+ $line = (int) $matches[2];
+ $phpcsFile->addErrorOnLine("PHP syntax error: $error", $line, 'PHPSyntax');
+ }
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that all uses of TRUE, FALSE and NULL are uppercase.
+ *
+ * @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\Standards\Generic\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class UpperCaseConstantSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_TRUE,
+ T_FALSE,
+ T_NULL,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $keyword = $tokens[$stackPtr]['content'];
+ $expected = strtoupper($keyword);
+ if ($keyword !== $expected) {
+ if ($keyword === strtolower($keyword)) {
+ $phpcsFile->recordMetric($stackPtr, 'PHP constant case', 'lower');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'PHP constant case', 'mixed');
+ }
+
+ $error = 'TRUE, FALSE and NULL must be uppercase; expected "%s" but found "%s"';
+ $data = array(
+ $expected,
+ $keyword,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, $expected);
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'PHP constant case', 'upper');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that two strings are not concatenated together; suggests using one string instead.
+ *
+ * @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\Standards\Generic\Sniffs\Strings;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class UnnecessaryStringConcatSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+ /**
+ * If true, an error will be thrown; otherwise a warning.
+ *
+ * @var boolean
+ */
+ public $error = true;
+
+ /**
+ * If true, strings concatenated over multiple lines are allowed.
+ *
+ * Useful if you break strings over multiple lines to work
+ * within a max line length.
+ *
+ * @var boolean
+ */
+ public $allowMultiline = false;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_STRING_CONCAT,
+ T_PLUS,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ // Work out which type of file this is for.
+ $tokens = $phpcsFile->getTokens();
+ if ($tokens[$stackPtr]['code'] === T_STRING_CONCAT) {
+ if ($phpcsFile->tokenizerType === 'JS') {
+ return;
+ }
+ } else {
+ if ($phpcsFile->tokenizerType === 'PHP') {
+ return;
+ }
+ }
+
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($prev === false || $next === false) {
+ return;
+ }
+
+ if (isset(Tokens::$stringTokens[$tokens[$prev]['code']]) === true
+ && isset(Tokens::$stringTokens[$tokens[$next]['code']]) === true
+ ) {
+ if ($tokens[$prev]['content'][0] === $tokens[$next]['content'][0]) {
+ // Before we throw an error for PHP, allow strings to be
+ // combined if they would have < and ? next to each other because
+ // this trick is sometimes required in PHP strings.
+ if ($phpcsFile->tokenizerType === 'PHP') {
+ $prevChar = substr($tokens[$prev]['content'], -2, 1);
+ $nextChar = $tokens[$next]['content'][1];
+ $combined = $prevChar.$nextChar;
+ if ($combined === '?'.'>' || $combined === '<'.'?') {
+ return;
+ }
+ }
+
+ if ($this->allowMultiline === true
+ && $tokens[$prev]['line'] !== $tokens[$next]['line']
+ ) {
+ return;
+ }
+
+ $error = 'String concat is not required here; use a single string instead';
+ if ($this->error === true) {
+ $phpcsFile->addError($error, $stackPtr, 'Found');
+ } else {
+ $phpcsFile->addWarning($error, $stackPtr, 'Found');
+ }
+ }//end if
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tests that the correct Subversion properties are set.
+ *
+ * @author Jack Bates <ms419@freezone.co.uk>
+ * @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\Standards\Generic\Sniffs\VersionControl;
+
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class SubversionPropertiesSniff implements Sniff
+{
+
+ /**
+ * The Subversion properties that should be set.
+ *
+ * Key of array is the SVN property and the value is the
+ * exact value the property should have or NULL if the
+ * property should just be set but the value is not fixed.
+ *
+ * @var array
+ */
+ protected $properties = array(
+ 'svn:keywords' => 'Author Id Revision',
+ 'svn:eol-style' => 'native',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $path = $phpcsFile->getFileName();
+ $properties = $this->getProperties($path);
+ if ($properties === null) {
+ // Not under version control.
+ return ($phpcsFile->numTokens + 1);
+ }
+
+ $allProperties = ($properties + $this->properties);
+ foreach ($allProperties as $key => $value) {
+ if (isset($properties[$key]) === true
+ && isset($this->properties[$key]) === false
+ ) {
+ $error = 'Unexpected Subversion property "%s" = "%s"';
+ $data = array(
+ $key,
+ $properties[$key],
+ );
+ $phpcsFile->addError($error, $stackPtr, 'Unexpected', $data);
+ continue;
+ }
+
+ if (isset($properties[$key]) === false
+ && isset($this->properties[$key]) === true
+ ) {
+ $error = 'Missing Subversion property "%s" = "%s"';
+ $data = array(
+ $key,
+ $this->properties[$key],
+ );
+ $phpcsFile->addError($error, $stackPtr, 'Missing', $data);
+ continue;
+ }
+
+ if ($properties[$key] !== null
+ && $properties[$key] !== $this->properties[$key]
+ ) {
+ $error = 'Subversion property "%s" = "%s" does not match "%s"';
+ $data = array(
+ $key,
+ $properties[$key],
+ $this->properties[$key],
+ );
+ $phpcsFile->addError($error, $stackPtr, 'NoMatch', $data);
+ }
+ }//end foreach
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+ /**
+ * Returns the Subversion properties which are actually set on a path.
+ *
+ * Returns NULL if the file is not under version control.
+ *
+ * @param string $path The path to return Subversion properties on.
+ *
+ * @return array
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If Subversion properties file could
+ * not be opened.
+ */
+ protected function getProperties($path)
+ {
+ $properties = array();
+
+ $paths = array();
+ $paths[] = dirname($path).'/.svn/props/'.basename($path).'.svn-work';
+ $paths[] = dirname($path).'/.svn/prop-base/'.basename($path).'.svn-base';
+
+ $foundPath = false;
+ foreach ($paths as $path) {
+ if (file_exists($path) === true) {
+ $foundPath = true;
+
+ $handle = fopen($path, 'r');
+ if ($handle === false) {
+ $error = 'Error opening file; could not get Subversion properties';
+ throw new RuntimeException($error);
+ }
+
+ while (feof($handle) === false) {
+ // Read a key length line. Might be END, though.
+ $buffer = trim(fgets($handle));
+
+ // Check for the end of the hash.
+ if ($buffer === 'END') {
+ break;
+ }
+
+ // Now read that much into a buffer.
+ $key = fread($handle, substr($buffer, 2));
+
+ // Suck up extra newline after key data.
+ fgetc($handle);
+
+ // Read a value length line.
+ $buffer = trim(fgets($handle));
+
+ // Now read that much into a buffer.
+ $length = substr($buffer, 2);
+ if ($length === '0') {
+ // Length of value is ZERO characters, so
+ // value is actually empty.
+ $value = '';
+ } else {
+ $value = fread($handle, $length);
+ }
+
+ // Suck up extra newline after value data.
+ fgetc($handle);
+
+ $properties[$key] = $value;
+ }//end while
+
+ fclose($handle);
+ }//end if
+ }//end foreach
+
+ if ($foundPath === false) {
+ return null;
+ }
+
+ return $properties;
+
+ }//end getProperties()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Throws errors if spaces are used for indentation other than precision indentation.
+ *
+ * @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\Standards\Generic\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DisallowSpaceIndentSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ 'CSS',
+ );
+
+ /**
+ * The --tab-width CLI value that is being used.
+ *
+ * @var integer
+ */
+ private $tabWidth = null;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ if ($this->tabWidth === null) {
+ if (isset($phpcsFile->config->tabWidth) === false || $phpcsFile->config->tabWidth === 0) {
+ // We have no idea how wide tabs are, so assume 4 spaces for fixing.
+ // It shouldn't really matter because indent checks elsewhere in the
+ // standard should fix things up.
+ $this->tabWidth = 4;
+ } else {
+ $this->tabWidth = $phpcsFile->config->tabWidth;
+ }
+ }
+
+ $checkTokens = array(
+ T_WHITESPACE => true,
+ T_INLINE_HTML => true,
+ T_DOC_COMMENT_WHITESPACE => true,
+ );
+
+ $tokens = $phpcsFile->getTokens();
+ for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) {
+ if ($tokens[$i]['column'] !== 1 || isset($checkTokens[$tokens[$i]['code']]) === false) {
+ continue;
+ }
+
+ // If tabs are being converted to spaces by the tokeniser, the
+ // original content should be checked instead of the converted content.
+ if (isset($tokens[$i]['orig_content']) === true) {
+ $content = $tokens[$i]['orig_content'];
+ } else {
+ $content = $tokens[$i]['content'];
+ }
+
+ $recordMetrics = true;
+
+ // If this is an inline HTML token, split the content into
+ // indentation whitespace and the actual HTML/text.
+ $nonWhitespace = '';
+ if ($tokens[$i]['code'] === T_INLINE_HTML && preg_match('`^(\s*)(\S.*)`s', $content, $matches) > 0) {
+ if (isset($matches[1]) === true) {
+ $content = $matches[1];
+ }
+
+ if (isset($matches[2]) === true) {
+ $nonWhitespace = $matches[2];
+ }
+ } else if (isset($tokens[($i + 1)]) === true
+ && $tokens[$i]['line'] < $tokens[($i + 1)]['line']
+ ) {
+ // There is no content after this whitespace except for a newline.
+ $content = rtrim($content, "\r\n");
+ $nonWhitespace = $phpcsFile->eolChar;
+
+ // Don't record metrics for empty lines.
+ $recordMetrics = false;
+ }
+
+ $hasSpaces = strpos($content, ' ');
+ $hasTabs = strpos($content, "\t");
+
+ if ($hasSpaces === false && $hasTabs === false) {
+ // Empty line.
+ continue;
+ }
+
+ if ($hasSpaces === false && $hasTabs !== false) {
+ // All ok, nothing to do.
+ if ($recordMetrics === true) {
+ $phpcsFile->recordMetric($i, 'Line indent', 'tabs');
+ }
+
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_DOC_COMMENT_WHITESPACE && $content === ' ') {
+ // Ignore file/class-level docblocks, especially for recording metrics.
+ continue;
+ }
+
+ // OK, by now we know there will be spaces.
+ // We just don't know yet whether they need to be replaced or
+ // are precision indentation, nor whether they are correctly
+ // placed at the end of the whitespace.
+ $trimmed = str_replace(' ', '', $content);
+ $numSpaces = (strlen($content) - strlen($trimmed));
+ $numTabs = (int) floor($numSpaces / $this->tabWidth);
+ $tabAfterSpaces = strpos($content, "\t", $hasSpaces);
+
+ if ($hasTabs === false) {
+ if ($recordMetrics === true) {
+ $phpcsFile->recordMetric($i, 'Line indent', 'spaces');
+ }
+
+ if ($numTabs === 0) {
+ // Ignore: precision indentation.
+ continue;
+ }
+ } else {
+ if ($numTabs === 0) {
+ // Precision indentation.
+ if ($recordMetrics === true) {
+ if ($tabAfterSpaces !== false) {
+ $phpcsFile->recordMetric($i, 'Line indent', 'mixed');
+ } else {
+ $phpcsFile->recordMetric($i, 'Line indent', 'tabs');
+ }
+ }
+
+ if ($tabAfterSpaces === false) {
+ // Ignore: precision indentation is already at the
+ // end of the whitespace.
+ continue;
+ }
+ } else if ($recordMetrics === true) {
+ $phpcsFile->recordMetric($i, 'Line indent', 'mixed');
+ }
+ }//end if
+
+ $error = 'Tabs must be used to indent lines; spaces are not allowed';
+ $fix = $phpcsFile->addFixableError($error, $i, 'SpacesUsed');
+ if ($fix === true) {
+ $remaining = ($numSpaces % $this->tabWidth);
+ $padding = str_repeat("\t", $numTabs);
+ $padding .= str_repeat(' ', $remaining);
+ $phpcsFile->fixer->replaceToken($i, $trimmed.$padding.$nonWhitespace);
+ }
+ }//end for
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Throws errors if tabs are used for indentation.
+ *
+ * @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\Standards\Generic\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DisallowTabIndentSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ 'CSS',
+ );
+
+ /**
+ * The --tab-width CLI value that is being used.
+ *
+ * @var integer
+ */
+ private $tabWidth = null;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ if ($this->tabWidth === null) {
+ if (isset($phpcsFile->config->tabWidth) === false || $phpcsFile->config->tabWidth === 0) {
+ // We have no idea how wide tabs are, so assume 4 spaces for metrics.
+ $this->tabWidth = 4;
+ } else {
+ $this->tabWidth = $phpcsFile->config->tabWidth;
+ }
+ }
+
+ $tokens = $phpcsFile->getTokens();
+ $error = 'Spaces must be used to indent lines; tabs are not allowed';
+ $errorCode = 'TabsUsed';
+
+ $checkTokens = array(
+ T_WHITESPACE => true,
+ T_INLINE_HTML => true,
+ T_DOC_COMMENT_WHITESPACE => true,
+ T_DOC_COMMENT_STRING => true,
+ );
+
+ for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) {
+ if (isset($checkTokens[$tokens[$i]['code']]) === false) {
+ continue;
+ }
+
+ // If tabs are being converted to spaces by the tokeniser, the
+ // original content should be checked instead of the converted content.
+ if (isset($tokens[$i]['orig_content']) === true) {
+ $content = $tokens[$i]['orig_content'];
+ } else {
+ $content = $tokens[$i]['content'];
+ }
+
+ if ($content === '') {
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_DOC_COMMENT_WHITESPACE && $content === ' ') {
+ // Ignore file/class-level DocBlock, especially for recording metrics.
+ continue;
+ }
+
+ $recordMetrics = true;
+ if (isset($tokens[($i + 1)]) === true
+ && $tokens[$i]['line'] < $tokens[($i + 1)]['line']
+ ) {
+ // Don't record metrics for empty lines.
+ $recordMetrics = false;
+ }
+
+ $tabFound = false;
+ if ($tokens[$i]['column'] === 1) {
+ if ($content[0] === "\t") {
+ $tabFound = true;
+ if ($recordMetrics === true) {
+ $spacePosition = strpos($content, ' ');
+ $tabAfterSpaces = strpos($content, "\t", $spacePosition);
+ if ($spacePosition !== false && $tabAfterSpaces !== false) {
+ $phpcsFile->recordMetric($i, 'Line indent', 'mixed');
+ } else {
+ // Check for use of precision spaces.
+ $trimmed = str_replace(' ', '', $content);
+ $numSpaces = (strlen($content) - strlen($trimmed));
+ $numTabs = (int) floor($numSpaces / $this->tabWidth);
+ if ($numTabs === 0) {
+ $phpcsFile->recordMetric($i, 'Line indent', 'tabs');
+ } else {
+ $phpcsFile->recordMetric($i, 'Line indent', 'mixed');
+ }
+ }
+ }
+ } else if ($content[0] === ' ') {
+ if (strpos($content, "\t") !== false) {
+ if ($recordMetrics === true) {
+ $phpcsFile->recordMetric($i, 'Line indent', 'mixed');
+ }
+
+ $tabFound = true;
+ } else if ($recordMetrics === true) {
+ $phpcsFile->recordMetric($i, 'Line indent', 'spaces');
+ }
+ }//end if
+ } else {
+ // Look for tabs so we can report and replace, but don't
+ // record any metrics about them because they aren't
+ // line indent tokens.
+ if (strpos($content, "\t") !== false) {
+ $tabFound = true;
+ $error = 'Spaces must be used for alignment; tabs are not allowed';
+ $errorCode = 'NonIndentTabsUsed';
+ }
+ }//end if
+
+ if ($tabFound === false) {
+ continue;
+ }
+
+ $fix = $phpcsFile->addFixableError($error, $i, $errorCode);
+ if ($fix === true) {
+ if (isset($tokens[$i]['orig_content']) === true) {
+ // Use the replacement that PHPCS has already done.
+ $phpcsFile->fixer->replaceToken($i, $tokens[$i]['content']);
+ } else {
+ // Replace tabs with spaces, using an indent of 4 spaces.
+ // Other sniffs can then correct the indent if they need to.
+ $newContent = str_replace("\t", ' ', $tokens[$i]['content']);
+ $phpcsFile->fixer->replaceToken($i, $newContent);
+ }
+ }
+ }//end for
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that control structures are defined and indented correctly.
+ *
+ * @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\Standards\Generic\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Config;
+
+class ScopeIndentSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+ /**
+ * The number of spaces code should be indented.
+ *
+ * @var integer
+ */
+ public $indent = 4;
+
+ /**
+ * Does the indent need to be exactly right?
+ *
+ * If TRUE, indent needs to be exactly $indent spaces. If FALSE,
+ * indent needs to be at least $indent spaces (but can be more).
+ *
+ * @var boolean
+ */
+ public $exact = false;
+
+ /**
+ * Should tabs be used for indenting?
+ *
+ * If TRUE, fixes will be made using tabs instead of spaces.
+ * The size of each tab is important, so it should be specified
+ * using the --tab-width CLI argument.
+ *
+ * @var boolean
+ */
+ public $tabIndent = false;
+
+ /**
+ * The --tab-width CLI value that is being used.
+ *
+ * @var integer
+ */
+ private $tabWidth = null;
+
+ /**
+ * List of tokens not needing to be checked for indentation.
+ *
+ * Useful to allow Sniffs based on this to easily ignore/skip some
+ * tokens from verification. For example, inline HTML sections
+ * or PHP open/close tags can escape from here and have their own
+ * rules elsewhere.
+ *
+ * @var int[]
+ */
+ public $ignoreIndentationTokens = array();
+
+ /**
+ * List of tokens not needing to be checked for indentation.
+ *
+ * This is a cached copy of the public version of this var, which
+ * can be set in a ruleset file, and some core ignored tokens.
+ *
+ * @var int[]
+ */
+ private $ignoreIndentation = array();
+
+ /**
+ * Any scope openers that should not cause an indent.
+ *
+ * @var int[]
+ */
+ protected $nonIndentingScopes = array();
+
+ /**
+ * Show debug output for this sniff.
+ *
+ * @var boolean
+ */
+ private $debug = false;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ if (defined('PHP_CODESNIFFER_IN_TESTS') === true) {
+ $this->debug = false;
+ }
+
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $debug = Config::getConfigData('scope_indent_debug');
+ if ($debug !== null) {
+ $this->debug = (bool) $debug;
+ }
+
+ if ($this->tabWidth === null) {
+ if (isset($phpcsFile->config->tabWidth) === false || $phpcsFile->config->tabWidth === 0) {
+ // We have no idea how wide tabs are, so assume 4 spaces for fixing.
+ // It shouldn't really matter because indent checks elsewhere in the
+ // standard should fix things up.
+ $this->tabWidth = 4;
+ } else {
+ $this->tabWidth = $phpcsFile->config->tabWidth;
+ }
+ }
+
+ $currentIndent = 0;
+ $lastOpenTag = $stackPtr;
+ $lastCloseTag = null;
+ $openScopes = array();
+ $adjustments = array();
+ $setIndents = array();
+
+ $tokens = $phpcsFile->getTokens();
+ $first = $phpcsFile->findFirstOnLine(T_INLINE_HTML, $stackPtr);
+ $trimmed = ltrim($tokens[$first]['content']);
+ if ($trimmed === '') {
+ $currentIndent = ($tokens[$stackPtr]['column'] - 1);
+ } else {
+ $currentIndent = (strlen($tokens[$first]['content']) - strlen($trimmed));
+ }
+
+ if ($this->debug === true) {
+ $line = $tokens[$stackPtr]['line'];
+ echo "Start with token $stackPtr on line $line with indent $currentIndent".PHP_EOL;
+ }
+
+ if (empty($this->ignoreIndentation) === true) {
+ $this->ignoreIndentation = array(T_INLINE_HTML => true);
+ foreach ($this->ignoreIndentationTokens as $token) {
+ if (is_int($token) === false) {
+ if (defined($token) === false) {
+ continue;
+ }
+
+ $token = constant($token);
+ }
+
+ $this->ignoreIndentation[$token] = true;
+ }
+ }//end if
+
+ $this->exact = (bool) $this->exact;
+ $this->tabIndent = (bool) $this->tabIndent;
+
+ for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) {
+ if ($i === false) {
+ // Something has gone very wrong; maybe a parse error.
+ break;
+ }
+
+ $checkToken = null;
+ $checkIndent = null;
+
+ $exact = (bool) $this->exact;
+ if ($exact === true && isset($tokens[$i]['nested_parenthesis']) === true) {
+ // Don't check indents exactly between parenthesis as they
+ // tend to have custom rules, such as with multi-line function calls
+ // and control structure conditions.
+ $exact = false;
+ }
+
+ // Detect line changes and figure out where the indent is.
+ if ($tokens[$i]['column'] === 1) {
+ $trimmed = ltrim($tokens[$i]['content']);
+ if ($trimmed === '') {
+ if (isset($tokens[($i + 1)]) === true
+ && $tokens[$i]['line'] === $tokens[($i + 1)]['line']
+ ) {
+ $checkToken = ($i + 1);
+ $tokenIndent = ($tokens[($i + 1)]['column'] - 1);
+ }
+ } else {
+ $checkToken = $i;
+ $tokenIndent = (strlen($tokens[$i]['content']) - strlen($trimmed));
+ }
+ }
+
+ // Closing parenthesis should just be indented to at least
+ // the same level as where they were opened (but can be more).
+ if (($checkToken !== null
+ && $tokens[$checkToken]['code'] === T_CLOSE_PARENTHESIS
+ && isset($tokens[$checkToken]['parenthesis_opener']) === true)
+ || ($tokens[$i]['code'] === T_CLOSE_PARENTHESIS
+ && isset($tokens[$i]['parenthesis_opener']) === true)
+ ) {
+ if ($checkToken !== null) {
+ $parenCloser = $checkToken;
+ } else {
+ $parenCloser = $i;
+ }
+
+ if ($this->debug === true) {
+ $line = $tokens[$i]['line'];
+ echo "Closing parenthesis found on line $line".PHP_EOL;
+ }
+
+ $parenOpener = $tokens[$parenCloser]['parenthesis_opener'];
+ if ($tokens[$parenCloser]['line'] !== $tokens[$parenOpener]['line']) {
+ $parens = 0;
+ if (isset($tokens[$parenCloser]['nested_parenthesis']) === true
+ && empty($tokens[$parenCloser]['nested_parenthesis']) === false
+ ) {
+ end($tokens[$parenCloser]['nested_parenthesis']);
+ $parens = key($tokens[$parenCloser]['nested_parenthesis']);
+ if ($this->debug === true) {
+ $line = $tokens[$parens]['line'];
+ echo "\t* token has nested parenthesis $parens on line $line *".PHP_EOL;
+ }
+ }
+
+ $condition = 0;
+ if (isset($tokens[$parenCloser]['conditions']) === true
+ && empty($tokens[$parenCloser]['conditions']) === false
+ ) {
+ end($tokens[$parenCloser]['conditions']);
+ $condition = key($tokens[$parenCloser]['conditions']);
+ if ($this->debug === true) {
+ $line = $tokens[$condition]['line'];
+ $type = $tokens[$condition]['type'];
+ echo "\t* token is inside condition $condition ($type) on line $line *".PHP_EOL;
+ }
+ }
+
+ if ($parens > $condition) {
+ if ($this->debug === true) {
+ echo "\t* using parenthesis *".PHP_EOL;
+ }
+
+ $parenOpener = $parens;
+ $condition = 0;
+ } else if ($condition > 0) {
+ if ($this->debug === true) {
+ echo "\t* using condition *".PHP_EOL;
+ }
+
+ $parenOpener = $condition;
+ $parens = 0;
+ }
+
+ $exact = false;
+
+ $lastOpenTagConditions = array_keys($tokens[$lastOpenTag]['conditions']);
+ $lastOpenTagCondition = array_pop($lastOpenTagConditions);
+
+ if ($condition > 0 && $lastOpenTagCondition === $condition) {
+ if ($this->debug === true) {
+ echo "\t* open tag is inside condition; using open tag *".PHP_EOL;
+ }
+
+ $checkIndent = ($tokens[$lastOpenTag]['column'] - 1);
+ if (isset($adjustments[$condition]) === true) {
+ $checkIndent += $adjustments[$condition];
+ }
+
+ $currentIndent = $checkIndent;
+
+ if ($this->debug === true) {
+ $type = $tokens[$lastOpenTag]['type'];
+ echo "\t=> checking indent of $checkIndent; main indent set to $currentIndent by token $lastOpenTag ($type)".PHP_EOL;
+ }
+ } else if ($condition > 0
+ && isset($tokens[$condition]['scope_opener']) === true
+ && isset($setIndents[$tokens[$condition]['scope_opener']]) === true
+ ) {
+ $checkIndent = $setIndents[$tokens[$condition]['scope_opener']];
+ if (isset($adjustments[$condition]) === true) {
+ $checkIndent += $adjustments[$condition];
+ }
+
+ $currentIndent = $checkIndent;
+
+ if ($this->debug === true) {
+ $type = $tokens[$condition]['type'];
+ echo "\t=> checking indent of $checkIndent; main indent set to $currentIndent by token $condition ($type)".PHP_EOL;
+ }
+ } else {
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $parenOpener, true);
+
+ $checkIndent = ($tokens[$first]['column'] - 1);
+ if (isset($adjustments[$first]) === true) {
+ $checkIndent += $adjustments[$first];
+ }
+
+ if ($this->debug === true) {
+ $line = $tokens[$first]['line'];
+ $type = $tokens[$first]['type'];
+ echo "\t* first token on line $line is $first ($type) *".PHP_EOL;
+ }
+
+ if ($first === $tokens[$parenCloser]['parenthesis_opener']) {
+ // This is unlikely to be the start of the statement, so look
+ // back further to find it.
+ $first--;
+ }
+
+ $prev = $phpcsFile->findStartOfStatement($first, T_COMMA);
+ if ($prev !== $first) {
+ // This is not the start of the statement.
+ if ($this->debug === true) {
+ $line = $tokens[$prev]['line'];
+ $type = $tokens[$prev]['type'];
+ echo "\t* previous is $type on line $line *".PHP_EOL;
+ }
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $prev, true);
+ $prev = $phpcsFile->findStartOfStatement($first, T_COMMA);
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $prev, true);
+ if ($this->debug === true) {
+ $line = $tokens[$first]['line'];
+ $type = $tokens[$first]['type'];
+ echo "\t* amended first token is $first ($type) on line $line *".PHP_EOL;
+ }
+ }
+
+ if (isset($tokens[$first]['scope_closer']) === true
+ && $tokens[$first]['scope_closer'] === $first
+ ) {
+ if ($this->debug === true) {
+ echo "\t* first token is a scope closer *".PHP_EOL;
+ }
+
+ if (isset($tokens[$first]['scope_condition']) === true) {
+ $scopeCloser = $first;
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $tokens[$scopeCloser]['scope_condition'], true);
+
+ $currentIndent = ($tokens[$first]['column'] - 1);
+ if (isset($adjustments[$first]) === true) {
+ $currentIndent += $adjustments[$first];
+ }
+
+ // Make sure it is divisible by our expected indent.
+ if ($tokens[$tokens[$scopeCloser]['scope_condition']]['code'] !== T_CLOSURE) {
+ $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
+ }
+
+ $setIndents[$first] = $currentIndent;
+
+ if ($this->debug === true) {
+ $type = $tokens[$first]['type'];
+ echo "\t=> indent set to $currentIndent by token $first ($type)".PHP_EOL;
+ }
+ }//end if
+ } else {
+ // Don't force current indent to divisible because there could be custom
+ // rules in place between parenthesis, such as with arrays.
+ $currentIndent = ($tokens[$first]['column'] - 1);
+ if (isset($adjustments[$first]) === true) {
+ $currentIndent += $adjustments[$first];
+ }
+
+ $setIndents[$first] = $currentIndent;
+
+ if ($this->debug === true) {
+ $type = $tokens[$first]['type'];
+ echo "\t=> checking indent of $checkIndent; main indent set to $currentIndent by token $first ($type)".PHP_EOL;
+ }
+ }//end if
+ }//end if
+ } else if ($this->debug === true) {
+ echo "\t * ignoring single-line definition *".PHP_EOL;
+ }//end if
+ }//end if
+
+ // Closing short array bracket should just be indented to at least
+ // the same level as where it was opened (but can be more).
+ if ($tokens[$i]['code'] === T_CLOSE_SHORT_ARRAY
+ || ($checkToken !== null
+ && $tokens[$checkToken]['code'] === T_CLOSE_SHORT_ARRAY)
+ ) {
+ if ($checkToken !== null) {
+ $arrayCloser = $checkToken;
+ } else {
+ $arrayCloser = $i;
+ }
+
+ if ($this->debug === true) {
+ $line = $tokens[$arrayCloser]['line'];
+ echo "Closing short array bracket found on line $line".PHP_EOL;
+ }
+
+ $arrayOpener = $tokens[$arrayCloser]['bracket_opener'];
+ if ($tokens[$arrayCloser]['line'] !== $tokens[$arrayOpener]['line']) {
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $arrayOpener, true);
+ $checkIndent = ($tokens[$first]['column'] - 1);
+ if (isset($adjustments[$first]) === true) {
+ $checkIndent += $adjustments[$first];
+ }
+
+ $exact = false;
+
+ if ($this->debug === true) {
+ $line = $tokens[$first]['line'];
+ $type = $tokens[$first]['type'];
+ echo "\t* first token on line $line is $first ($type) *".PHP_EOL;
+ }
+
+ if ($first === $tokens[$arrayCloser]['bracket_opener']) {
+ // This is unlikely to be the start of the statement, so look
+ // back further to find it.
+ $first--;
+ }
+
+ $prev = $phpcsFile->findStartOfStatement($first, array(T_COMMA, T_DOUBLE_ARROW));
+ if ($prev !== $first) {
+ // This is not the start of the statement.
+ if ($this->debug === true) {
+ $line = $tokens[$prev]['line'];
+ $type = $tokens[$prev]['type'];
+ echo "\t* previous is $type on line $line *".PHP_EOL;
+ }
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $prev, true);
+ $prev = $phpcsFile->findStartOfStatement($first, array(T_COMMA, T_DOUBLE_ARROW));
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $prev, true);
+ if ($this->debug === true) {
+ $line = $tokens[$first]['line'];
+ $type = $tokens[$first]['type'];
+ echo "\t* amended first token is $first ($type) on line $line *".PHP_EOL;
+ }
+ } else if ($tokens[$first]['code'] === T_WHITESPACE) {
+ $first = $phpcsFile->findNext(T_WHITESPACE, ($first + 1), null, true);
+ }
+
+ if (isset($tokens[$first]['scope_closer']) === true
+ && $tokens[$first]['scope_closer'] === $first
+ ) {
+ // The first token is a scope closer and would have already
+ // been processed and set the indent level correctly, so
+ // don't adjust it again.
+ if ($this->debug === true) {
+ echo "\t* first token is a scope closer; ignoring closing short array bracket *".PHP_EOL;
+ }
+
+ if (isset($setIndents[$first]) === true) {
+ $currentIndent = $setIndents[$first];
+ if ($this->debug === true) {
+ echo "\t=> indent reset to $currentIndent".PHP_EOL;
+ }
+ }
+ } else {
+ // Don't force current indent to be divisible because there could be custom
+ // rules in place for arrays.
+ $currentIndent = ($tokens[$first]['column'] - 1);
+ if (isset($adjustments[$first]) === true) {
+ $currentIndent += $adjustments[$first];
+ }
+
+ $setIndents[$first] = $currentIndent;
+
+ if ($this->debug === true) {
+ $type = $tokens[$first]['type'];
+ echo "\t=> checking indent of $checkIndent; main indent set to $currentIndent by token $first ($type)".PHP_EOL;
+ }
+ }//end if
+ } else if ($this->debug === true) {
+ echo "\t * ignoring single-line definition *".PHP_EOL;
+ }//end if
+ }//end if
+
+ // Adjust lines within scopes while auto-fixing.
+ if ($checkToken !== null
+ && $exact === false
+ && (empty($tokens[$checkToken]['conditions']) === false
+ || (isset($tokens[$checkToken]['scope_opener']) === true
+ && $tokens[$checkToken]['scope_opener'] === $checkToken))
+ ) {
+ if (empty($tokens[$checkToken]['conditions']) === false) {
+ end($tokens[$checkToken]['conditions']);
+ $condition = key($tokens[$checkToken]['conditions']);
+ } else {
+ $condition = $tokens[$checkToken]['scope_condition'];
+ }
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $condition, true);
+
+ if (isset($adjustments[$first]) === true
+ && (($adjustments[$first] < 0 && $tokenIndent > $currentIndent)
+ || ($adjustments[$first] > 0 && $tokenIndent < $currentIndent))
+ ) {
+ $padding = ($tokenIndent + $adjustments[$first]);
+ if ($padding > 0) {
+ if ($this->tabIndent === true) {
+ $numTabs = floor($padding / $this->tabWidth);
+ $numSpaces = ($padding - ($numTabs * $this->tabWidth));
+ $padding = str_repeat("\t", $numTabs).str_repeat(' ', $numSpaces);
+ } else {
+ $padding = str_repeat(' ', $padding);
+ }
+ } else {
+ $padding = '';
+ }
+
+ if ($phpcsFile->fixer->enabled === true) {
+ if ($checkToken === $i) {
+ $phpcsFile->fixer->replaceToken($checkToken, $padding.$trimmed);
+ } else {
+ // Easier to just replace the entire indent.
+ $phpcsFile->fixer->replaceToken(($checkToken - 1), $padding);
+ }
+ }
+
+ if ($this->debug === true) {
+ $length = strlen($padding);
+ $line = $tokens[$checkToken]['line'];
+ $type = $tokens[$checkToken]['type'];
+ echo "Indent adjusted to $length for $type on line $line".PHP_EOL;
+ }
+
+ $adjustments[$checkToken] = $adjustments[$first];
+
+ if ($this->debug === true) {
+ $line = $tokens[$checkToken]['line'];
+ $type = $tokens[$checkToken]['type'];
+ echo "\t=> Add adjustment of ".$adjustments[$checkToken]." for token $checkToken ($type) on line $line".PHP_EOL;
+ }
+ }//end if
+ }//end if
+
+ // Scope closers reset the required indent to the same level as the opening condition.
+ if (($checkToken !== null
+ && isset($openScopes[$checkToken]) === true
+ || (isset($tokens[$checkToken]['scope_condition']) === true
+ && isset($tokens[$checkToken]['scope_closer']) === true
+ && $tokens[$checkToken]['scope_closer'] === $checkToken
+ && $tokens[$checkToken]['line'] !== $tokens[$tokens[$checkToken]['scope_opener']]['line']))
+ || ($checkToken === null
+ && isset($openScopes[$i]) === true
+ || (isset($tokens[$i]['scope_condition']) === true
+ && isset($tokens[$i]['scope_closer']) === true
+ && $tokens[$i]['scope_closer'] === $i
+ && $tokens[$i]['line'] !== $tokens[$tokens[$i]['scope_opener']]['line']))
+ ) {
+ if ($this->debug === true) {
+ if ($checkToken === null) {
+ $type = $tokens[$tokens[$i]['scope_condition']]['type'];
+ $line = $tokens[$i]['line'];
+ } else {
+ $type = $tokens[$tokens[$checkToken]['scope_condition']]['type'];
+ $line = $tokens[$checkToken]['line'];
+ }
+
+ echo "Close scope ($type) on line $line".PHP_EOL;
+ }
+
+ $scopeCloser = $checkToken;
+ if ($scopeCloser === null) {
+ $scopeCloser = $i;
+ } else {
+ array_pop($openScopes);
+ }
+
+ if (isset($tokens[$scopeCloser]['scope_condition']) === true) {
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $tokens[$scopeCloser]['scope_condition'], true);
+ if ($this->debug === true) {
+ $line = $tokens[$first]['line'];
+ $type = $tokens[$first]['type'];
+ echo "\t* first token is $first ($type) on line $line *".PHP_EOL;
+ }
+
+ while ($tokens[$first]['code'] === T_CONSTANT_ENCAPSED_STRING
+ && $tokens[($first - 1)]['code'] === T_CONSTANT_ENCAPSED_STRING
+ ) {
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, ($first - 1), true);
+ if ($this->debug === true) {
+ $line = $tokens[$first]['line'];
+ $type = $tokens[$first]['type'];
+ echo "\t* found multi-line string; amended first token is $first ($type) on line $line *".PHP_EOL;
+ }
+ }
+
+ $currentIndent = ($tokens[$first]['column'] - 1);
+ if (isset($adjustments[$first]) === true) {
+ $currentIndent += $adjustments[$first];
+ }
+
+ // Make sure it is divisible by our expected indent.
+ if ($tokens[$tokens[$scopeCloser]['scope_condition']]['code'] !== T_CLOSURE) {
+ $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
+ }
+
+ $setIndents[$scopeCloser] = $currentIndent;
+
+ if ($this->debug === true) {
+ $type = $tokens[$scopeCloser]['type'];
+ echo "\t=> indent set to $currentIndent by token $scopeCloser ($type)".PHP_EOL;
+ }
+
+ // We only check the indent of scope closers if they are
+ // curly braces because other constructs tend to have different rules.
+ if ($tokens[$scopeCloser]['code'] === T_CLOSE_CURLY_BRACKET) {
+ $exact = true;
+ } else {
+ $checkToken = null;
+ }
+ }//end if
+ }//end if
+
+ // Handle scope for JS object notation.
+ if ($phpcsFile->tokenizerType === 'JS'
+ && (($checkToken !== null
+ && $tokens[$checkToken]['code'] === T_CLOSE_OBJECT
+ && $tokens[$checkToken]['line'] !== $tokens[$tokens[$checkToken]['bracket_opener']]['line'])
+ || ($checkToken === null
+ && $tokens[$i]['code'] === T_CLOSE_OBJECT
+ && $tokens[$i]['line'] !== $tokens[$tokens[$i]['bracket_opener']]['line']))
+ ) {
+ if ($this->debug === true) {
+ $line = $tokens[$i]['line'];
+ echo "Close JS object on line $line".PHP_EOL;
+ }
+
+ $scopeCloser = $checkToken;
+ if ($scopeCloser === null) {
+ $scopeCloser = $i;
+ } else {
+ array_pop($openScopes);
+ }
+
+ $parens = 0;
+ if (isset($tokens[$scopeCloser]['nested_parenthesis']) === true
+ && empty($tokens[$scopeCloser]['nested_parenthesis']) === false
+ ) {
+ end($tokens[$scopeCloser]['nested_parenthesis']);
+ $parens = key($tokens[$scopeCloser]['nested_parenthesis']);
+ if ($this->debug === true) {
+ $line = $tokens[$parens]['line'];
+ echo "\t* token has nested parenthesis $parens on line $line *".PHP_EOL;
+ }
+ }
+
+ $condition = 0;
+ if (isset($tokens[$scopeCloser]['conditions']) === true
+ && empty($tokens[$scopeCloser]['conditions']) === false
+ ) {
+ end($tokens[$scopeCloser]['conditions']);
+ $condition = key($tokens[$scopeCloser]['conditions']);
+ if ($this->debug === true) {
+ $line = $tokens[$condition]['line'];
+ $type = $tokens[$condition]['type'];
+ echo "\t* token is inside condition $condition ($type) on line $line *".PHP_EOL;
+ }
+ }
+
+ if ($parens > $condition) {
+ if ($this->debug === true) {
+ echo "\t* using parenthesis *".PHP_EOL;
+ }
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $parens, true);
+ $condition = 0;
+ } else if ($condition > 0) {
+ if ($this->debug === true) {
+ echo "\t* using condition *".PHP_EOL;
+ }
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $condition, true);
+ $parens = 0;
+ } else {
+ if ($this->debug === true) {
+ $line = $tokens[$tokens[$scopeCloser]['bracket_opener']]['line'];
+ echo "\t* token is not in parenthesis or condition; using opener on line $line *".PHP_EOL;
+ }
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $tokens[$scopeCloser]['bracket_opener'], true);
+ }//end if
+
+ $currentIndent = ($tokens[$first]['column'] - 1);
+ if (isset($adjustments[$first]) === true) {
+ $currentIndent += $adjustments[$first];
+ }
+
+ if ($parens > 0 || $condition > 0) {
+ $checkIndent = ($tokens[$first]['column'] - 1);
+ if (isset($adjustments[$first]) === true) {
+ $checkIndent += $adjustments[$first];
+ }
+
+ if ($condition > 0) {
+ $checkIndent += $this->indent;
+ $currentIndent += $this->indent;
+ $exact = true;
+ }
+ } else {
+ $checkIndent = $currentIndent;
+ }
+
+ // Make sure it is divisible by our expected indent.
+ $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
+ $checkIndent = (int) (ceil($checkIndent / $this->indent) * $this->indent);
+ $setIndents[$first] = $currentIndent;
+
+ if ($this->debug === true) {
+ $type = $tokens[$first]['type'];
+ echo "\t=> checking indent of $checkIndent; main indent set to $currentIndent by token $first ($type)".PHP_EOL;
+ }
+ }//end if
+
+ if ($checkToken !== null
+ && isset(Tokens::$scopeOpeners[$tokens[$checkToken]['code']]) === true
+ && in_array($tokens[$checkToken]['code'], $this->nonIndentingScopes) === false
+ && isset($tokens[$checkToken]['scope_opener']) === true
+ ) {
+ $exact = true;
+
+ $lastOpener = null;
+ if (empty($openScopes) === false) {
+ end($openScopes);
+ $lastOpener = current($openScopes);
+ }
+
+ // A scope opener that shares a closer with another token (like multiple
+ // CASEs using the same BREAK) needs to reduce the indent level so its
+ // indent is checked correctly. It will then increase the indent again
+ // (as all openers do) after being checked.
+ if ($lastOpener !== null
+ && isset($tokens[$lastOpener]['scope_closer']) === true
+ && $tokens[$lastOpener]['level'] === $tokens[$checkToken]['level']
+ && $tokens[$lastOpener]['scope_closer'] === $tokens[$checkToken]['scope_closer']
+ ) {
+ $currentIndent -= $this->indent;
+ $setIndents[$lastOpener] = $currentIndent;
+ if ($this->debug === true) {
+ $line = $tokens[$i]['line'];
+ $type = $tokens[$lastOpener]['type'];
+ echo "Shared closer found on line $line".PHP_EOL;
+ echo "\t=> indent set to $currentIndent by token $lastOpener ($type)".PHP_EOL;
+ }
+ }
+
+ if ($tokens[$checkToken]['code'] === T_CLOSURE
+ && $tokenIndent > $currentIndent
+ ) {
+ // The opener is indented more than needed, which is fine.
+ // But just check that it is divisible by our expected indent.
+ $checkIndent = (int) (ceil($tokenIndent / $this->indent) * $this->indent);
+ $exact = false;
+
+ if ($this->debug === true) {
+ $line = $tokens[$i]['line'];
+ echo "Closure found on line $line".PHP_EOL;
+ echo "\t=> checking indent of $checkIndent; main indent remains at $currentIndent".PHP_EOL;
+ }
+ }
+ }//end if
+
+ // Method prefix indentation has to be exact or else if will break
+ // the rest of the function declaration, and potentially future ones.
+ if ($checkToken !== null
+ && isset(Tokens::$methodPrefixes[$tokens[$checkToken]['code']]) === true
+ && $tokens[($checkToken + 1)]['code'] !== T_DOUBLE_COLON
+ ) {
+ $exact = true;
+ }
+
+ // JS property indentation has to be exact or else if will break
+ // things like function and object indentation.
+ if ($checkToken !== null && $tokens[$checkToken]['code'] === T_PROPERTY) {
+ $exact = true;
+ }
+
+ // PHP tags needs to be indented to exact column positions
+ // so they don't cause problems with indent checks for the code
+ // within them, but they don't need to line up with the current indent.
+ if ($checkToken !== null
+ && ($tokens[$checkToken]['code'] === T_OPEN_TAG
+ || $tokens[$checkToken]['code'] === T_OPEN_TAG_WITH_ECHO
+ || $tokens[$checkToken]['code'] === T_CLOSE_TAG)
+ ) {
+ $exact = true;
+ $checkIndent = ($tokens[$checkToken]['column'] - 1);
+ $checkIndent = (int) (ceil($checkIndent / $this->indent) * $this->indent);
+ }
+
+ // Special case for ELSE statements that are not on the same
+ // line as the previous IF statements closing brace. They still need
+ // to have the same indent or it will break code after the block.
+ if ($checkToken !== null && $tokens[$checkToken]['code'] === T_ELSE) {
+ $exact = true;
+ }
+
+ if ($checkIndent === null) {
+ $checkIndent = $currentIndent;
+ }
+
+ /*
+ The indent of the line is checked by the following IF block.
+
+ Up until now, we've just been figuring out what the indent
+ of this line should be.
+
+ After this IF block, we adjust the indent again for
+ the checking of future line.
+ */
+
+ $adjusted = false;
+ if ($checkToken !== null
+ && isset($this->ignoreIndentation[$tokens[$checkToken]['code']]) === false
+ && (($tokenIndent !== $checkIndent && $exact === true)
+ || ($tokenIndent < $checkIndent && $exact === false))
+ ) {
+ $type = 'IncorrectExact';
+ $error = 'Line indented incorrectly; expected ';
+ if ($exact === false) {
+ $error .= 'at least ';
+ $type = 'Incorrect';
+ }
+
+ if ($this->tabIndent === true) {
+ $error .= '%s tabs, found %s';
+ $data = array(
+ floor($checkIndent / $this->tabWidth),
+ floor($tokenIndent / $this->tabWidth),
+ );
+ } else {
+ $error .= '%s spaces, found %s';
+ $data = array(
+ $checkIndent,
+ $tokenIndent,
+ );
+ }
+
+ if ($this->debug === true) {
+ $line = $tokens[$checkToken]['line'];
+ $message = vsprintf($error, $data);
+ echo "[Line $line] $message".PHP_EOL;
+ }
+
+ $fix = $phpcsFile->addFixableError($error, $checkToken, $type, $data);
+ if ($fix === true || $this->debug === true) {
+ $padding = '';
+ if ($this->tabIndent === true) {
+ $numTabs = floor($checkIndent / $this->tabWidth);
+ if ($numTabs > 0) {
+ $numSpaces = ($checkIndent - ($numTabs * $this->tabWidth));
+ $padding = str_repeat("\t", $numTabs).str_repeat(' ', $numSpaces);
+ }
+ } else if ($checkIndent > 0) {
+ $padding = str_repeat(' ', $checkIndent);
+ }
+
+ if ($checkToken === $i) {
+ $accepted = $phpcsFile->fixer->replaceToken($checkToken, $padding.$trimmed);
+ } else {
+ // Easier to just replace the entire indent.
+ $accepted = $phpcsFile->fixer->replaceToken(($checkToken - 1), $padding);
+ }
+
+ if ($accepted === true) {
+ $adjustments[$checkToken] = ($checkIndent - $tokenIndent);
+ if ($this->debug === true) {
+ $line = $tokens[$checkToken]['line'];
+ $type = $tokens[$checkToken]['type'];
+ echo "\t=> Add adjustment of ".$adjustments[$checkToken]." for token $checkToken ($type) on line $line".PHP_EOL;
+ }
+ }
+ } else {
+ // Assume the change would be applied and continue
+ // checking indents under this assumption. This gives more
+ // technically accurate error messages.
+ $adjustments[$checkToken] = ($checkIndent - $tokenIndent);
+ }//end if
+ }//end if
+
+ if ($checkToken !== null) {
+ $i = $checkToken;
+ }
+
+ // Completely skip here/now docs as the indent is a part of the
+ // content itself.
+ if ($tokens[$i]['code'] === T_START_HEREDOC
+ || $tokens[$i]['code'] === T_START_NOWDOC
+ ) {
+ $i = $phpcsFile->findNext(array(T_END_HEREDOC, T_END_NOWDOC), ($i + 1));
+ continue;
+ }
+
+ // Completely skip multi-line strings as the indent is a part of the
+ // content itself.
+ if ($tokens[$i]['code'] === T_CONSTANT_ENCAPSED_STRING
+ || $tokens[$i]['code'] === T_DOUBLE_QUOTED_STRING
+ ) {
+ $i = $phpcsFile->findNext($tokens[$i]['code'], ($i + 1), null, true);
+ $i--;
+ continue;
+ }
+
+ // Completely skip doc comments as they tend to have complex
+ // indentation rules.
+ if ($tokens[$i]['code'] === T_DOC_COMMENT_OPEN_TAG) {
+ $i = $tokens[$i]['comment_closer'];
+ continue;
+ }
+
+ // Open tags reset the indent level.
+ if ($tokens[$i]['code'] === T_OPEN_TAG
+ || $tokens[$i]['code'] === T_OPEN_TAG_WITH_ECHO
+ ) {
+ if ($this->debug === true) {
+ $line = $tokens[$i]['line'];
+ echo "Open PHP tag found on line $line".PHP_EOL;
+ }
+
+ if ($checkToken === null) {
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $i, true);
+ $currentIndent = (strlen($tokens[$first]['content']) - strlen(ltrim($tokens[$first]['content'])));
+ } else {
+ $currentIndent = ($tokens[$i]['column'] - 1);
+ }
+
+ $lastOpenTag = $i;
+
+ if (isset($adjustments[$i]) === true) {
+ $currentIndent += $adjustments[$i];
+ }
+
+ // Make sure it is divisible by our expected indent.
+ $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
+ $setIndents[$i] = $currentIndent;
+
+ if ($this->debug === true) {
+ $type = $tokens[$i]['type'];
+ echo "\t=> indent set to $currentIndent by token $i ($type)".PHP_EOL;
+ }
+
+ continue;
+ }//end if
+
+ // Close tags reset the indent level, unless they are closing a tag
+ // opened on the same line.
+ if ($tokens[$i]['code'] === T_CLOSE_TAG) {
+ if ($this->debug === true) {
+ $line = $tokens[$i]['line'];
+ echo "Close PHP tag found on line $line".PHP_EOL;
+ }
+
+ if ($tokens[$lastOpenTag]['line'] !== $tokens[$i]['line']) {
+ $currentIndent = ($tokens[$i]['column'] - 1);
+ $lastCloseTag = $i;
+ } else {
+ if ($lastCloseTag === null) {
+ $currentIndent = 0;
+ } else {
+ $currentIndent = ($tokens[$lastCloseTag]['column'] - 1);
+ }
+ }
+
+ if (isset($adjustments[$i]) === true) {
+ $currentIndent += $adjustments[$i];
+ }
+
+ // Make sure it is divisible by our expected indent.
+ $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
+ $setIndents[$i] = $currentIndent;
+
+ if ($this->debug === true) {
+ $type = $tokens[$i]['type'];
+ echo "\t=> indent set to $currentIndent by token $i ($type)".PHP_EOL;
+ }
+
+ continue;
+ }//end if
+
+ // Anon classes and functions set the indent based on their own indent level.
+ if ($tokens[$i]['code'] === T_CLOSURE || $tokens[$i]['code'] === T_ANON_CLASS) {
+ $closer = $tokens[$i]['scope_closer'];
+ if ($tokens[$i]['line'] === $tokens[$closer]['line']) {
+ if ($this->debug === true) {
+ $type = str_replace('_', ' ', strtolower(substr($tokens[$i]['type'], 2)));
+ $line = $tokens[$i]['line'];
+ echo "* ignoring single-line $type on line $line".PHP_EOL;
+ }
+
+ $i = $closer;
+ continue;
+ }
+
+ if ($this->debug === true) {
+ $type = str_replace('_', ' ', strtolower(substr($tokens[$i]['type'], 2)));
+ $line = $tokens[$i]['line'];
+ echo "Open $type on line $line".PHP_EOL;
+ }
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $i, true);
+ if ($this->debug === true) {
+ $line = $tokens[$first]['line'];
+ $type = $tokens[$first]['type'];
+ echo "\t* first token is $first ($type) on line $line *".PHP_EOL;
+ }
+
+ while ($tokens[$first]['code'] === T_CONSTANT_ENCAPSED_STRING
+ && $tokens[($first - 1)]['code'] === T_CONSTANT_ENCAPSED_STRING
+ ) {
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, ($first - 1), true);
+ if ($this->debug === true) {
+ $line = $tokens[$first]['line'];
+ $type = $tokens[$first]['type'];
+ echo "\t* found multi-line string; amended first token is $first ($type) on line $line *".PHP_EOL;
+ }
+ }
+
+ $currentIndent = (($tokens[$first]['column'] - 1) + $this->indent);
+ $openScopes[$tokens[$i]['scope_closer']] = $tokens[$i]['scope_condition'];
+
+ if (isset($adjustments[$first]) === true) {
+ $currentIndent += $adjustments[$first];
+ }
+
+ // Make sure it is divisible by our expected indent.
+ $currentIndent = (int) (floor($currentIndent / $this->indent) * $this->indent);
+ $i = $tokens[$i]['scope_opener'];
+ $setIndents[$i] = $currentIndent;
+
+ if ($this->debug === true) {
+ $type = $tokens[$i]['type'];
+ echo "\t=> indent set to $currentIndent by token $i ($type)".PHP_EOL;
+ }
+
+ continue;
+ }//end if
+
+ // Scope openers increase the indent level.
+ if (isset($tokens[$i]['scope_condition']) === true
+ && isset($tokens[$i]['scope_opener']) === true
+ && $tokens[$i]['scope_opener'] === $i
+ ) {
+ $closer = $tokens[$i]['scope_closer'];
+ if ($tokens[$i]['line'] === $tokens[$closer]['line']) {
+ if ($this->debug === true) {
+ $line = $tokens[$i]['line'];
+ $type = $tokens[$i]['type'];
+ echo "* ignoring single-line $type on line $line".PHP_EOL;
+ }
+
+ $i = $closer;
+ continue;
+ }
+
+ $condition = $tokens[$tokens[$i]['scope_condition']]['code'];
+ if (isset(Tokens::$scopeOpeners[$condition]) === true
+ && in_array($condition, $this->nonIndentingScopes) === false
+ ) {
+ if ($this->debug === true) {
+ $line = $tokens[$i]['line'];
+ $type = $tokens[$tokens[$i]['scope_condition']]['type'];
+ echo "Open scope ($type) on line $line".PHP_EOL;
+ }
+
+ $currentIndent += $this->indent;
+ $setIndents[$i] = $currentIndent;
+ $openScopes[$tokens[$i]['scope_closer']] = $tokens[$i]['scope_condition'];
+
+ if ($this->debug === true) {
+ $type = $tokens[$i]['type'];
+ echo "\t=> indent set to $currentIndent by token $i ($type)".PHP_EOL;
+ }
+
+ continue;
+ }
+ }//end if
+
+ // JS objects set the indent level.
+ if ($phpcsFile->tokenizerType === 'JS'
+ && $tokens[$i]['code'] === T_OBJECT
+ ) {
+ $closer = $tokens[$i]['bracket_closer'];
+ if ($tokens[$i]['line'] === $tokens[$closer]['line']) {
+ if ($this->debug === true) {
+ $line = $tokens[$i]['line'];
+ echo "* ignoring single-line JS object on line $line".PHP_EOL;
+ }
+
+ $i = $closer;
+ continue;
+ }
+
+ if ($this->debug === true) {
+ $line = $tokens[$i]['line'];
+ echo "Open JS object on line $line".PHP_EOL;
+ }
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $i, true);
+ $currentIndent = (($tokens[$first]['column'] - 1) + $this->indent);
+ if (isset($adjustments[$first]) === true) {
+ $currentIndent += $adjustments[$first];
+ }
+
+ // Make sure it is divisible by our expected indent.
+ $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
+ $setIndents[$first] = $currentIndent;
+
+ if ($this->debug === true) {
+ $type = $tokens[$first]['type'];
+ echo "\t=> indent set to $currentIndent by token $first ($type)".PHP_EOL;
+ }
+
+ continue;
+ }//end if
+
+ // Closing an anon class or function.
+ if (isset($tokens[$i]['scope_condition']) === true
+ && $tokens[$i]['scope_closer'] === $i
+ && ($tokens[$tokens[$i]['scope_condition']]['code'] === T_CLOSURE
+ || $tokens[$tokens[$i]['scope_condition']]['code'] === T_ANON_CLASS)
+ ) {
+ if ($this->debug === true) {
+ $type = str_replace('_', ' ', strtolower(substr($tokens[$tokens[$i]['scope_condition']]['type'], 2)));
+ $line = $tokens[$i]['line'];
+ echo "Close $type on line $line".PHP_EOL;
+ }
+
+ $prev = false;
+
+ $object = 0;
+ if ($phpcsFile->tokenizerType === 'JS') {
+ $conditions = $tokens[$i]['conditions'];
+ krsort($conditions, SORT_NUMERIC);
+ foreach ($conditions as $token => $condition) {
+ if ($condition === T_OBJECT) {
+ $object = $token;
+ break;
+ }
+ }
+
+ if ($this->debug === true && $object !== 0) {
+ $line = $tokens[$object]['line'];
+ echo "\t* token is inside JS object $object on line $line *".PHP_EOL;
+ }
+ }
+
+ $parens = 0;
+ if (isset($tokens[$i]['nested_parenthesis']) === true
+ && empty($tokens[$i]['nested_parenthesis']) === false
+ ) {
+ end($tokens[$i]['nested_parenthesis']);
+ $parens = key($tokens[$i]['nested_parenthesis']);
+ if ($this->debug === true) {
+ $line = $tokens[$parens]['line'];
+ echo "\t* token has nested parenthesis $parens on line $line *".PHP_EOL;
+ }
+ }
+
+ $condition = 0;
+ if (isset($tokens[$i]['conditions']) === true
+ && empty($tokens[$i]['conditions']) === false
+ ) {
+ end($tokens[$i]['conditions']);
+ $condition = key($tokens[$i]['conditions']);
+ if ($this->debug === true) {
+ $line = $tokens[$condition]['line'];
+ $type = $tokens[$condition]['type'];
+ echo "\t* token is inside condition $condition ($type) on line $line *".PHP_EOL;
+ }
+ }
+
+ if ($parens > $object && $parens > $condition) {
+ if ($this->debug === true) {
+ echo "\t* using parenthesis *".PHP_EOL;
+ }
+
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($parens - 1), null, true);
+ $object = 0;
+ $condition = 0;
+ } else if ($object > 0 && $object >= $condition) {
+ if ($this->debug === true) {
+ echo "\t* using object *".PHP_EOL;
+ }
+
+ $prev = $object;
+ $parens = 0;
+ $condition = 0;
+ } else if ($condition > 0) {
+ if ($this->debug === true) {
+ echo "\t* using condition *".PHP_EOL;
+ }
+
+ $prev = $condition;
+ $object = 0;
+ $parens = 0;
+ }//end if
+
+ if ($prev === false) {
+ $prev = $phpcsFile->findPrevious(array(T_EQUAL, T_RETURN), ($tokens[$i]['scope_condition'] - 1), null, false, null, true);
+ if ($prev === false) {
+ $prev = $i;
+ if ($this->debug === true) {
+ echo "\t* could not find a previous T_EQUAL or T_RETURN token; will use current token *".PHP_EOL;
+ }
+ }
+ }
+
+ if ($this->debug === true) {
+ $line = $tokens[$prev]['line'];
+ $type = $tokens[$prev]['type'];
+ echo "\t* previous token is $type on line $line *".PHP_EOL;
+ }
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $prev, true);
+ if ($this->debug === true) {
+ $line = $tokens[$first]['line'];
+ $type = $tokens[$first]['type'];
+ echo "\t* first token on line $line is $first ($type) *".PHP_EOL;
+ }
+
+ $prev = $phpcsFile->findStartOfStatement($first);
+ if ($prev !== $first) {
+ // This is not the start of the statement.
+ if ($this->debug === true) {
+ $line = $tokens[$prev]['line'];
+ $type = $tokens[$prev]['type'];
+ echo "\t* amended previous is $type on line $line *".PHP_EOL;
+ }
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $prev, true);
+ if ($this->debug === true) {
+ $line = $tokens[$first]['line'];
+ $type = $tokens[$first]['type'];
+ echo "\t* amended first token is $first ($type) on line $line *".PHP_EOL;
+ }
+ }
+
+ $currentIndent = ($tokens[$first]['column'] - 1);
+ if ($object > 0 || $condition > 0) {
+ $currentIndent += $this->indent;
+ }
+
+ if (isset($tokens[$first]['scope_closer']) === true
+ && $tokens[$first]['scope_closer'] === $first
+ ) {
+ if ($this->debug === true) {
+ echo "\t* first token is a scope closer *".PHP_EOL;
+ }
+
+ if ($condition === 0 || $tokens[$condition]['scope_opener'] < $first) {
+ $currentIndent = $setIndents[$first];
+ } else if ($this->debug === true) {
+ echo "\t* ignoring scope closer *".PHP_EOL;
+ }
+ }
+
+ // Make sure it is divisible by our expected indent.
+ $currentIndent = (int) (ceil($currentIndent / $this->indent) * $this->indent);
+ $setIndents[$first] = $currentIndent;
+
+ if ($this->debug === true) {
+ $type = $tokens[$first]['type'];
+ echo "\t=> indent set to $currentIndent by token $first ($type)".PHP_EOL;
+ }
+ }//end if
+ }//end for
+
+ // Don't process the rest of the file.
+ return $phpcsFile->numTokens;
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+$var = array();
+$var = [1,2,3];
+$var = array(1,2,3);
+echo $var[1];
+$foo = array($var[1],$var[2]);
+$foo = array(
+ 1
+ 2
+ 3
+ );
+$var = array/*comment*/(1,2,3);
+$var = array;
+
+function foo(array $array) {}
+
+function foo(array $array) use ($foo): array {}
+
+abstract function foo(): array;
+
+abstract function foo(): Foo\Bar;
+
+abstract function foo(): \Foo\Bar;
--- /dev/null
+<?php
+$var = [];
+$var = [1,2,3];
+$var = [1,2,3];
+echo $var[1];
+$foo = [$var[1],$var[2]];
+$foo = [
+ 1
+ 2
+ 3
+ ];
+$var = /*comment*/[1,2,3];
+$var = [];
+
+function foo(array $array) {}
+
+function foo(array $array) use ($foo): array {}
+
+abstract function foo(): array;
+
+abstract function foo(): Foo\Bar;
+
+abstract function foo(): \Foo\Bar;
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowLongArraySyntax 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\Standards\Generic\Tests\Arrays;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowLongArraySyntaxUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 4 => 1,
+ 6 => 1,
+ 7 => 1,
+ 12 => 1,
+ 13 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$var = array();
+$var = [];
+$var = array(1,2,3);
+$var = [1,2,3];
+echo $var[1];
+$foo = [$var[1],$var[2]];
+$foo = [
+ 1
+ 2
+ 3
+ ];
\ No newline at end of file
--- /dev/null
+<?php
+$var = array();
+$var = array();
+$var = array(1,2,3);
+$var = array(1,2,3);
+echo $var[1];
+$foo = array($var[1],$var[2]);
+$foo = array(
+ 1
+ 2
+ 3
+ );
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowShortArraySyntax 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\Standards\Generic\Tests\Arrays;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowShortArraySyntaxUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 5 => 1,
+ 7 => 1,
+ 8 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class MyClass {}
+class YourClass {}
+interface MyInterface {}
+interface YourInterface {}
+class MyClass {}
+interface MyInterface {}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+class MyClass {}
+interface MyInterface {}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+namespace A\B {
+ class MyClass {}
+ interface MyInterface {}
+}
+
+namespace D {
+ class MyClass {}
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+namespace C;
+class MyClass {}
+interface MyInterface {}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+namespace C;
+class MyClass {}
+interface YourInterface {}
+
+namespace D;
+class MyClass {}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+class Test{
+ public function testOne(){
+ ?>
+ <p>some html here</p>
+ <?php
+ }
+}
+
+class Test{
+
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the DuplicateClassName 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\Standards\Generic\Tests\Classes;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DuplicateClassNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='')
+ {
+ switch ($testFile) {
+ case 'DuplicateClassNameUnitTest.1.inc':
+ return array(
+ 6 => 1,
+ 7 => 1,
+ );
+ break;
+ case 'DuplicateClassNameUnitTest.2.inc':
+ return array(
+ 2 => 1,
+ 3 => 1,
+ );
+ break;
+ case 'DuplicateClassNameUnitTest.5.inc':
+ return array(
+ 3 => 1,
+ 7 => 1,
+ );
+ break;
+ case 'DuplicateClassNameUnitTest.6.inc':
+ return array(10 => 1);
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// The following are all ok.
+interface Test_Interface_Ok_A {
+}
+
+class Test_Class_Ok_A {
+}
+
+class Test_Class_Ok_B extends Test_Class_Ok_A {
+}
+
+class Test_Class_Ok_C implements Test_Interface_Ok_A {
+}
+
+
+// These are all incorrect.
+interface Test_Interface_Bad_A
+{ // There should be no content after the brace.
+}
+
+class Test_Class_Bad_A
+ {
+ }
+
+class Test_Class_Bad_B extends Test_Class_Bad_A
+
+{ // There should be no content after the brace.
+}
+
+class Test_Class_Bad_C implements Test_Interface_Bad_A
+
+
+{
+}
+
+// These should all be flagged for wrong whitespace before opening brace.
+interface Test_Interface_Bad_C {
+}
+
+class Test_Class_Bad_G {
+}
+
+class Test_Class_Bad_H extends Test_Class_Bad_G {
+}
+
+class Test_Class_Bad_I implements Test_Interface_Bad_C{
+}
+
+// This one should be flagged as a potential parse error.
+class Test_Class_Bad_H
+
+// This is OK.
+class A_Class_With_Really_Long_Name
+ extends Another_Class_With_A_Really_Long_Name {
+
+}
+
+// This is OK too.
+class A_Class_With_Really_Long_Name_2
+ extends Another_Class_With_A_Really_Long_Name
+ implements Some_Interface_With_A_Long_Name,
+ Another_Interface_With_A_Long_Name {
+
+}
+
+// But this is not.
+class A_Class_With_Really_Long_Name_3
+ extends Another_Class_With_A_Really_Long_Name
+ {
+
+}
+
+// Nor is this.
+class A_Class_With_Really_Long_Name_4
+ extends Another_Class_With_A_Really_Long_Name
+ implements Some_Interface_With_A_Long_Name,
+ Another_Interface_With_A_Long_Name
+{
+
+}
+
+// While this is ok again.
+class Test_Class_Bad_G /*some comment*/ {
+}
+
+// And this is not.
+class Test_Class_Bad_G
+ /*some comment*/
+{
+}
--- /dev/null
+<?php
+
+// The following are all ok.
+interface Test_Interface_Ok_A {
+}
+
+class Test_Class_Ok_A {
+}
+
+class Test_Class_Ok_B extends Test_Class_Ok_A {
+}
+
+class Test_Class_Ok_C implements Test_Interface_Ok_A {
+}
+
+
+// These are all incorrect.
+interface Test_Interface_Bad_A {
+ // There should be no content after the brace.
+}
+
+class Test_Class_Bad_A {
+
+ }
+
+class Test_Class_Bad_B extends Test_Class_Bad_A {
+
+ // There should be no content after the brace.
+}
+
+class Test_Class_Bad_C implements Test_Interface_Bad_A {
+
+
+
+}
+
+// These should all be flagged for wrong whitespace before opening brace.
+interface Test_Interface_Bad_C {
+}
+
+class Test_Class_Bad_G {
+}
+
+class Test_Class_Bad_H extends Test_Class_Bad_G {
+}
+
+class Test_Class_Bad_I implements Test_Interface_Bad_C {
+}
+
+// This one should be flagged as a potential parse error.
+class Test_Class_Bad_H
+
+// This is OK.
+class A_Class_With_Really_Long_Name
+ extends Another_Class_With_A_Really_Long_Name {
+
+}
+
+// This is OK too.
+class A_Class_With_Really_Long_Name_2
+ extends Another_Class_With_A_Really_Long_Name
+ implements Some_Interface_With_A_Long_Name,
+ Another_Interface_With_A_Long_Name {
+
+}
+
+// But this is not.
+class A_Class_With_Really_Long_Name_3
+ extends Another_Class_With_A_Really_Long_Name {
+
+
+}
+
+// Nor is this.
+class A_Class_With_Really_Long_Name_4
+ extends Another_Class_With_A_Really_Long_Name
+ implements Some_Interface_With_A_Long_Name,
+ Another_Interface_With_A_Long_Name {
+
+
+}
+
+// While this is ok again.
+class Test_Class_Bad_G /*some comment*/ {
+}
+
+// And this is not.
+class Test_Class_Bad_G
+ /*some comment*/ {
+
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the OpeningBraceSameLine 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\Standards\Generic\Tests\Classes;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class OpeningBraceSameLineUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+
+ return array(
+ 19 => 2,
+ 23 => 1,
+ 28 => 2,
+ 34 => 1,
+ 38 => 1,
+ 41 => 1,
+ 44 => 1,
+ 47 => 1,
+ 70 => 1,
+ 79 => 1,
+ 90 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(51 => 1);
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// OK.
+if ($a === 123) {
+} elseif ($a == 123) {
+} elseif ($a !== 123) {
+} elseif ($a != 123) {}
+
+function abc( $a = 'default' ) {}
+if (in_array( $a, array( 1 => 'a', 2 => 'b' ) ) ) {}
+
+switch ( $a === $b ) {}
+switch ( true ) {
+ case $sample == 'something':
+ break;
+}
+
+for ( $i = 0; $i == 100; $i++ ) {}
+for ( $i = 0; $i >= 100; $i++ ) {}
+for ( $i = 0; ; $i++ ) {}
+for (;;) {}
+
+do {
+} while ( $sample == false );
+
+while ( $sample === false ) {}
+
+ // Silly, but not an assignment.
+if (123 = $a) {}
+if (strtolower($b) = $b) {}
+if (array( 1 => 'a', 2 => 'b' ) = $b) {}
+
+if (SOME_CONSTANT = 123) {
+} else if(self::SOME_CONSTANT -= 10) {}
+
+if ( $a() = 123 ) {
+} else if ( $b->something() = 123 ) {
+} elseif ( $c::something() = 123 ) {}
+
+switch ( true ) {
+ case 'something' = $sample:
+ break;
+}
+
+// Assignments in condition.
+if ($a = 123) {
+} elseif ($a = 'abc') {
+} else if( $a += 10 ) {
+} else if($a -= 10) {
+} else if($a *= 10) {
+} else if($a **= 10) {
+} else if($a /= 10) {
+} else if($a .= strtolower($b)) {
+} else if($a %= SOME_CONSTANT) {
+} else if($a &= 2) {
+} else if($a |= 2) {
+} else if($a ^= 2) {
+} else if($a <<= 2) {
+} else if($a >>= 2) {
+} else if($a ??= $b) {
+} elseif( $a = 'abc' && $b = 'def' ) {
+} elseif(
+ $a = 'abc'
+ && $a .= 'def'
+) {}
+
+if ($a[] = 123) {
+} elseif ($a['something'] = 123) {
+} elseif (self::$a = 123) {
+} elseif (parent::$a *= 123) {
+} elseif (static::$a = 123) {
+} elseif (MyClass::$a .= 'abc') {
+} else if( $this->something += 10 ) {}
+
+switch ( $a = $b ) {}
+switch ( true ) {
+ case $sample = 'something':
+ break;
+
+ case $sample = 'something' && $a = $b:
+ break;
+}
+
+for ( $i = 0; $i = 100; $i++ ) {}
+for ( $i = 0; $i = 100 && $b = false; $i++ ) {}
+
+do {
+} while ( $sample = false );
+
+while ( $sample = false ) {}
+
+if ($a = 123) :
+endif;
--- /dev/null
+<?php
+/**
+ * Unit test class for the AssignmentInCondition sniff.
+ *
+ * @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
+ * @copyright 2017 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\CodeAnalysis;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class AssignmentInConditionUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 46 => 1,
+ 47 => 1,
+ 48 => 1,
+ 49 => 1,
+ 50 => 1,
+ 51 => 1,
+ 52 => 1,
+ 53 => 1,
+ 54 => 1,
+ 55 => 1,
+ 56 => 1,
+ 57 => 1,
+ 58 => 1,
+ 59 => 1,
+ 60 => 1,
+ 61 => 2,
+ 63 => 1,
+ 64 => 1,
+ 67 => 1,
+ 68 => 1,
+ 69 => 1,
+ 70 => 1,
+ 71 => 1,
+ 72 => 1,
+ 73 => 1,
+ 75 => 1,
+ 77 => 1,
+ 80 => 2,
+ 84 => 1,
+ 85 => 2,
+ 88 => 1,
+ 90 => 1,
+ 92 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+switch ($foo) {
+ // Empty switch statement body
+}
+
+switch ($foo) {
+ case 'bar':
+ break;
+ default:
+ break;
+}
+
+
+if ($foo) {
+ // Just a comment
+} elseif ($bar) {
+ // Yet another comment
+} else {
+
+}
+
+if ($foo) {
+ $foo = 'bar';
+} else if ($bar) {
+ $bar = 'foo';
+}
+
+for ($i = 0; $i < 10; $i++) {
+ for ($j = 0; $j < 10; $j++) {
+ // Just a comment
+ }
+}
+
+foreach ($foo as $bar) {}
+
+foreach ($foo as $bar) {
+ $bar *= 2;
+}
+
+do {
+ // Just a comment
+ // Just another comment
+} while ($foo);
+
+do {
+ while ($bar) {
+
+ }
+} while (true);
+
+while ($foo) { /* Comment in the same line */ }
+
+while ($foo) {
+ try {
+
+ } catch (Exception $e) {
+ echo $e->getTraceAsString();
+ }
+}
+
+try {
+ throw Exception('Error...');
+} catch (Exception $e) {}
+
+try {
+ throw Exception('Error...');
+} catch (Exception $e) {
+ // TODO: Handle this exception later :-)
+}
+
+if (true) {} elseif (false) {}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the EmptyStatement sniff.
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\CodeAnalysis;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class EmptyStatementUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 15 => 1,
+ 17 => 1,
+ 19 => 1,
+ 30 => 1,
+ 35 => 1,
+ 41 => 1,
+ 47 => 1,
+ 52 => 1,
+ 55 => 1,
+ 64 => 1,
+ 68 => 1,
+ 72 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+for ($i = 0; $i < 10; $i++) {
+ // Everything is fine
+}
+
+for (; $it->valid();) {
+ $it->next();
+}
+
+for (;(($it1->valid() && $foo) || (!$it2->value && ($bar || false)));/*Could be ingored*/) {
+ $it1->next();
+ $it2->next();
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ForLoopShouldBeWhileLoop sniff.
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\CodeAnalysis;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ForLoopShouldBeWhileLoopUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 6 => 1,
+ 10 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+$a = array(1, 2, 3, 4);
+for ($i = 0; $i < count($a); $i++) {
+ $a[$i] *= $i;
+}
+
+for ($i = 0, $c = sizeof($a); $i < $c; ++$i) {
+ $a[$i] *= $i;
+}
+
+$it = new ArrayIterator($a);
+for ($it->rewind(); $it->valid(); $it->next()) {
+ echo $it->current();
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ForLoopWithTestFunctionCall sniff.
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\CodeAnalysis;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ForLoopWithTestFunctionCallUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 4 => 1,
+ 13 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+for ($i = 0; $i < 20; $i++) {
+ for ($j = 0; $j < 5; $i += 2) {
+ for ($k = 0; $k > 3; $i++) {
+
+ }
+ }
+}
+
+for ($i = 0; $i < 20; $i++) {
+ for ($j = 0; $j < 5; $j += 2) {
+ for ($k = 0; $k > 3; $k++) {
+
+ }
+ }
+}
+
+for ($i = 0; $i < 20; $i++) {
+ for ($j = 0; $j < 5; $j += 2) {
+ for ($k = 0; $k > 3; $j++) {
+
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the JumbledIncrementer sniff.
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\CodeAnalysis;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class JumbledIncrementerUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 3 => 2,
+ 4 => 1,
+ 20 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+if (true) {
+
+} else if (false) {
+
+} elseif (true) {
+
+}
+
+if (file_exists(__FILE__) === true) {
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the UnconditionalIfStatement sniff.
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\CodeAnalysis;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class UnconditionalIfStatementUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 3 => 1,
+ 5 => 1,
+ 7 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+class Foo {
+ public final $FOOBAR = 23;
+ protected final $FOO = 42;
+ private final $BAR = 17;
+}
+
+final class Foo_Bar {
+ public $foobar;
+ public final $FOOBAR = 23;
+
+ protected $foo;
+ protected final $FOO = 42;
+
+ private $bar;
+ private final $BAR = 17;
+}
+
+final class Bar_Foo {
+ public $foobar;
+ protected $foo;
+ private $bar;
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the UnnecessaryFinalModifier sniff.
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\CodeAnalysis;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class UnnecessaryFinalModifierUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 11 => 1,
+ 14 => 1,
+ 17 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+function foo($a, $b) {
+ return $a * 2;
+}
+
+function baz($a, $b) {
+ echo "baz({$a});";
+}
+
+function bar($a, $b) {
+ $x = $b;
+ for ($i = 0; $i <$a; $i++) {
+ $x += $a * $i;
+ }
+ return $x;
+}
+
+function foobar($a, &$b) {
+ return (preg_match('/foo/', $a, $b) !== 0);
+}
+
+class Foo {
+ function barfoo($a, $b) {
+ // Empty body means interface method in many cases.
+ }
+
+ function fooBar($a, $b) {
+ return;
+ }
+}
+
+function foo($bar)
+{
+ print <<<BAZ
+ $bar
+BAZ
+}
+
+function foo( $parameter ) {
+ $wango = <<<HERE
+1 Must be a HEREdoc of at least one line
+HERE;
+ $x = $parameter; // This line must be immediately after the HERE; with no intervening blank lines.
+ $tango = <<<HERE
+1 Must be a HEREdoc
+2
+3
+4
+5
+6
+7
+8
+9 at least 9 lines long
+HERE;
+}
+
+function foo( $parameter ) {
+ return <<<HTML
+<?xml version="1.0"?>
+<value>$parameter</value>
+HTML;
+}
+
+print foo( 'PARAMETER' );
+print "\n";
+
+function foo($bar)
+{
+ print "${bar} things\n";
+}
+
+function bar($x)
+{
+ return 2 * ${x};
+}
+
+function ($a, $b) {
+ return $a * 2;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the UnusedFunctionParameter sniff.
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\CodeAnalysis;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class UnusedFunctionParameterUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 3 => 1,
+ 7 => 1,
+ 78 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+class FooBar {
+ public function __construct($a, $b) {
+ parent::__construct($a, $b);
+ }
+}
+
+class BarFoo {
+ public function __construct($a, $b) {
+ parent::__construct($a, 'XML', $b);
+ }
+}
+
+class Foo {
+ public function export($a, $b = null) {
+ return parent::export($a, $b);
+ }
+}
+
+class Bar {
+ public function export($a, $b = null) {
+ return parent::export($a);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the UselessOverridingMethod sniff.
+ *
+ * @author Manuel Pichler <mapi@manuel-pichler.de>
+ * @copyright 2007-2014 Manuel Pichler. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\CodeAnalysis;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class UselessOverridingMethodUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 4 => 1,
+ 16 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Short description.
+ *
+ * Long description
+ * over multiple lines.
+ *
+ * @tag1 one
+ * @tag2 two
+ * @tag3 three
+ */
+
+/**
+ * short description
+ *
+ * long description
+ * over multiple lines.
+ * @tag1 one
+ */
+
+/**
+ *
+ * Short description
+ *
+ *
+ * Long description
+ * over multiple lines
+ *
+ *
+ * @tag1 one
+ *
+ */
+
+/*
+ This is not a doc block.
+ */
+
+/** Short description.
+ *
+ * @tag one
+ * @tag2 two
+ * @tagThree three
+ * @tagFour four
+ */
+
+/**
+ * Short description.
+ *
+ * @tag one
+ *
+ * @param
+ * @param
+ *
+ */
+
+/**
+ * Short description.
+ * @param
+ * @param
+ * @tag one
+ */
+
+/**
+ * Short description.
+ *
+ *
+ * @param
+ *
+ * @param
+ *
+ *
+ * @tag one
+ */
+
+/**
+ * Short description.
+ *
+ * @param
+ *
+ * @tag one
+ * @param
+ */
+
+/**
+ * Short description.
+ *
+ * @groupOne one
+ * @groupOne two
+ *
+ * @group2 one
+ * @group2 two
+ *
+ *
+ * @g3
+ * @g3 two
+ */
+
+/**
+ * Short description
+ * over multiple lines.
+ *
+ * Long descrption.
+ *
+ * @param
+ *
+ * @tag one
+ */
+
+/**
+ * Short description.
+ *
+ * @tag1 one some comment across
+ * multiple lines
+ * @tag1 two some comment across
+ * multiple lines
+ * @tag1 three some comment across
+ * multiple lines
+ */
+
+/**
+ * Returns true if the specified string is in the camel caps format.
+ *
+ * @param boolean $classFormat If true, check to see if the string is in the
+ * class format. Class format strings must start
+ * with a capital letter and contain no
+ * underscores.
+ * @param boolean $strict If true, the string must not have two capital
+ * letters next to each other. If false, a
+ * relaxed camel caps policy is used to allow
+ * for acronyms.
+ *
+ * @return boolean
+ */
+
+/**
+ * Verifies that a @throws tag exists for a function that throws exceptions.
+ * Verifies the number of @throws tags and the number of throw tokens matches.
+ * Verifies the exception type.
+ *
+ * PHP version 5
+ *
+ * @category PHP
+ * @package PHP_CodeSniffer
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @author Marc McIntyre <mmcintyre@squiz.net>
+ * @copyright 2006-2012 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
+ */
+
+/**
+ * Comment
+ *
+ * @one
+ * @two
+ * @one
+ *
+ * @two something
+ * here
+ * @two foo
+ * @three something
+ * here
+ * @three bar
+ */
+
+/**
+ * @ var Comment
+ */
+
+/** @var Database $mockedDatabase */
+/** @var Container $mockedContainer */
+
+/**
+ * 这是一条测试评论.
+ */
+
+/**
+ * I'm a function short-description
+ * @return boolean
+ */
+
+/**
+ * this is a test
+ * @author test
+ * @param boolean $foo blah
+ * @return boolean
+ * @param boolean $bar Blah.
+ */
+
+/**
+ * étude des ...
+ */
+
+/**doc comment */
--- /dev/null
+
+/**
+ * Short description.
+ *
+ * Long description
+ * over multiple lines.
+ *
+ * @tag1 one
+ * @tag2 two
+ * @tag3 three
+ */
+
+/**
+ * short description
+ *
+ * long description
+ * over multiple lines.
+ * @tag1 one
+ */
+
+/**
+ *
+ * Short description
+ *
+ *
+ * Long description
+ * over multiple lines
+ *
+ *
+ * @tag1 one
+ *
+ */
+
+/*
+ This is not a doc block.
+ */
+
+/** Short description.
+ *
+ * @tag one
+ * @tag2 two
+ * @tagThree three
+ * @tagFour four
+ */
+
+/**
+ * Short description.
+ *
+ * @tag one
+ *
+ * @param
+ * @param
+ *
+ */
+
+/**
+ * Short description.
+ * @param
+ * @param
+ * @tag one
+ */
+
+/**
+ * Short description.
+ *
+ *
+ * @param
+ *
+ * @param
+ *
+ *
+ * @tag one
+ */
+
+ /**
+ * Short description.
+ *
+ * @param
+ *
+ * @tag one
+ * @param
+ */
+
+/**
+ * Short description.
+ *
+ * @groupOne one
+ * @groupOne two
+ *
+ * @group2 one
+ * @group2 two
+ *
+ *
+ * @g3
+ * @g3 two
+ */
+
+ /**
+ * Short description
+ * over multiple lines.
+ *
+ * Long descrption.
+ *
+ * @param
+ *
+ * @tag one
+ */
+
+/**
+ * Short description.
+ *
+ * @tag1 one some comment across
+ * multiple lines
+ * @tag1 two some comment across
+ * multiple lines
+ * @tag1 three some comment across
+ * multiple lines
+ */
+
+ /**
+ * Returns true if the specified string is in the camel caps format.
+ *
+ * @param boolean $classFormat If true, check to see if the string is in the
+ * class format. Class format strings must start
+ * with a capital letter and contain no
+ * underscores.
+ * @param boolean $strict If true, the string must not have two capital
+ * letters next to each other. If false, a
+ * relaxed camel caps policy is used to allow
+ * for acronyms.
+ *
+ * @return boolean
+ */
+
+ /**
+ * Verifies that a @throws tag exists for a function that throws exceptions.
+ * Verifies the number of @throws tags and the number of throw tokens matches.
+ * Verifies the exception type.
+ *
+ * PHP version 5
+ *
+ * @category PHP
+ * @package PHP_CodeSniffer
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @author Marc McIntyre <mmcintyre@squiz.net>
+ * @copyright 2006-2012 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
+ */
+
+ /**
+ * Comment
+ *
+ * @one
+ * @two
+ * @one
+ *
+ * @two something
+ * here
+ * @two foo
+ * @three something
+ * here
+ * @three bar
+ */
+
+ /**
+ * @ var Comment
+ */
+
+/** @var Database $mockedDatabase */
+/** @var Container $mockedContainer */
+
+/**
+ * 这是一条测试评论.
+ */
+
+/**
+ * I'm a function short-description
+ * @return boolean
+ */
+
+/**
+ * this is a test
+ * @author test
+ * @param boolean $foo blah
+ * @return boolean
+ * @param boolean $bar Blah.
+ */
+
+/**
+ * étude des ...
+ */
+
+/**doc comment */
--- /dev/null
+<?php
+/**
+ * Unit test class for the DocCommentSniff 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\Standards\Generic\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DocCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array(int => int)
+ */
+ public function getErrorList()
+ {
+ return array(
+ 14 => 1,
+ 16 => 1,
+ 18 => 1,
+ 23 => 1,
+ 26 => 1,
+ 30 => 1,
+ 32 => 1,
+ 38 => 2,
+ 40 => 1,
+ 41 => 1,
+ 51 => 1,
+ 54 => 1,
+ 58 => 1,
+ 60 => 2,
+ 67 => 1,
+ 69 => 2,
+ 80 => 1,
+ 81 => 2,
+ 88 => 1,
+ 91 => 1,
+ 95 => 1,
+ 156 => 1,
+ 158 => 1,
+ 170 => 3,
+ 171 => 3,
+ 179 => 1,
+ 183 => 1,
+ 184 => 1,
+ 185 => 2,
+ 186 => 1,
+ 187 => 2,
+ 191 => 1,
+ 194 => 4,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array(int => int)
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * FIXME: really need to fix this before I commit.
+ * FIXME
+ */
+
+// FIXME: this is broken, don't forget to fix it
+error_log('test');
+
+// FIXME remove this.
+Debug::bam('test');
+
+// fixme - fix this.
+
+// Extract info from the array.
+// FIXME: this is just a stub, fill in later
+
+// Extract info from the array (fixme: finish later)
+// To do this, use a function!
+// nofixme! NOFIXME! NOfixme!
+//FIXME.
+//éfixme
+//fixmeé
--- /dev/null
+
+/**
+ * FIXME: Write this comment
+ * FIXME
+ */
+
+// FIXME: remove this.
+alert('test');
+
+// FIXME remove this.
+alert('test');
+
+// fixme - remove this.
+
+// Extract info from the array.
+// FIXME: can this be done faster?
+
+// Extract info from the array (fixme: make it faster)
+// To do this, use a function!
+// nofixme! NOFIXME! NOfixme!
+//FIXME.
+//éfixme
+//fixmeé
--- /dev/null
+<?php
+/**
+ * Unit test class for the Fixme sniff.
+ *
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @author Sam Graham <php-codesniffer@illusori.co.uk>
+ * @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\Standards\Generic\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FixmeUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='FixmeUnitTest.inc')
+ {
+ return array(
+ 3 => 1,
+ 4 => 1,
+ 7 => 1,
+ 10 => 1,
+ 13 => 1,
+ 16 => 1,
+ 18 => 1,
+ 21 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='FixmeUnitTest.inc')
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * TODO: Write this comment
+ * TODO
+ */
+
+// TODO: remove this.
+error_log('test');
+
+// TODO remove this.
+Debug::bam('test');
+
+// todo - remove this.
+
+// Extract info from the array.
+// TODO: can this be done faster?
+
+// Extract info from the array (todo: make it faster)
+// To do this, use a function!
+// notodo! NOTODO! NOtodo!
+//TODO.
+//étodo
+//todoé
--- /dev/null
+
+/**
+ * TODO: Write this comment
+ * TODO
+ */
+
+// TODO: remove this.
+alert('test');
+
+// TODO remove this.
+alert('test');
+
+// todo - remove this.
+
+// Extract info from the array.
+// TODO: can this be done faster?
+
+// Extract info from the array (todo: make it faster)
+// To do this, use a function!
+// notodo! NOTODO! NOtodo!
+//TODO.
+//étodo
+//todoé
--- /dev/null
+<?php
+/**
+ * Unit test class for the Todo 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\Standards\Generic\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class TodoUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='TodoUnitTest.inc')
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='TodoUnitTest.inc')
+ {
+ return array(
+ 3 => 1,
+ 4 => 1,
+ 7 => 1,
+ 10 => 1,
+ 13 => 1,
+ 16 => 1,
+ 18 => 1,
+ 21 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+if ($something) echo 'hello';
+
+if ($something) {
+ echo 'hello';
+} else echo 'hi';
+
+if ($something) {
+ echo 'hello';
+} else if ($else) echo 'hi';
+
+foreach ($something as $thing) echo 'hello';
+
+for ($i; $i > 0; $i--) echo 'hello';
+
+while ($something) echo 'hello';
+
+do {
+ $i--;
+} while ($something);
+
+if(true)
+ $someObject->{$name};
+
+if (true) :
+ $foo = true;
+endif;
+
+while (true) :
+ $foo = true;
+endwhile;
+
+for ($i; $i > 0; $i--) :
+ echo 'hello';
+endfor;
+
+foreach ($array as $element) :
+ echo 'hello';
+endforeach;
+
+while (!$this->readLine($tokens, $tag));
+while (!$this->readLine($tokens, $tag)); //skip to end of file
+
+foreach ($cookies as $cookie)
+ if ($cookie->match($uri, $matchSessionCookies, $now))
+ $ret[] = $cookie;
+
+foreach ($stringParade as $hit)
+ $hitParade[] = $hit + 0; //cast to integer
+
+if ($foo) :
+ echo 'true';
+elseif ($something) :
+ echo 'foo';
+else:
+ echo 'false';
+endif;
+
+function test()
+{
+ if ($a)
+ $a.=' '.($b ? 'b' : ($c ? ($d ? 'd' : 'c') : ''));
+}
+
+if ($a)
+ foreach ($b as $c) {
+ if ($d) {
+ $e=$f;
+ $g=$h;
+ } elseif ($i==0) {
+ $j=$k;
+ }
+ }
+
+?>
+<div style="text-align: right;">
+ <?php if ($model->scenario == 'simple') $widget->renderPager() ?>
+</div>
+
+<?php
+switch ($this->error):
+ case Shop_Customer :: ERROR_INVALID_GENDER: ?>
+ Ungültiges Geschlecht!
+ <?php break;
+ case Shop_Customer :: ERROR_EMAIL_IN_USE: ?>
+ Die eingetragene E-Mail-Adresse ist bereits registriert.
+ <?php break;
+endswitch;
+
+if ($this->allowShopping !== true):
+ if ($this->status != Shop_Cart :: OK):
+ switch ($this->status):
+ case Shop_Cart :: NOT_FOUND:
+ echo 'foo';
+ endswitch;
+ endif;
+else:
+ echo 'foo';
+endif;
+
+// ELSE IF split over multiple lines (not inline)
+if ($test) {
+} else
+ if ($test) {
+ } else {
+ }
+
+switch($response = \Bar::baz('bat', function ($foo) {
+ return 'bar';
+})) {
+ case 1:
+ return 'test';
+
+ case 2:
+ return 'other';
+}
+
+$stuff = [1,2,3];
+foreach($stuff as $num)
+ if ($num %2 ) {
+ echo "even";
+ } else {
+ echo "odd";
+ }
+
+$i = 0;
+foreach($stuff as $num)
+ do {
+ echo $i;
+ $i++;
+ } while ($i < 5);
+
+foreach($stuff as $num)
+ if (true) {
+ echo "true1\n";
+ }
+ if (true) {
+ echo "true2\n";
+ }
+
+if ($foo) echo 'foo';
+elseif ($bar) echo 'bar';
+else echo 'baz';
+
+switch ($type) {
+ case 1:
+ if ($foo) {
+ return true;
+ } elseif ($baz)
+ return true;
+ else {
+ echo 'else';
+ }
+ break;
+}
+
+foreach ($sql as $s)
+ if (!$this->execute) echo "<pre>",$s.";\n</pre>";
+ else {
+ $ok = $this->connDest->Execute($s);
+ if (!$ok)
+ if ($this->neverAbort) $ret = false;
+ else return false;
+ }
+
+if ($bar)
+ if ($foo) echo 'hi'; // lol
+
+if ($level == 'district')
+ \DB::update(<<<EOD
+some
+text
+here
+EOD
+ );
+
+if ($level == 'district')
+ $var = <<<EOD
+some
+text
+here
+EOD;
+
+if ($a && $a === Foo::VARIABLE && ($a === Foo::METHOD || $a === Foo::FUNCTION))
+ echo 'hi';
+
+$out = array_map(function ($test) { if ($test) return 1; else return 2; }, $input); // comment
+
+for ($x=0;$x<5;$x++):
+ if ($x) continue;
+endfor;
+
+for ($x=0;$x<5;$x++):
+ if ($x) continue ?> <?php
+endfor;
+
+if (true)
+ try {
+ }
+ catch(Exception $e) {
+ }
+
+do {
+ $i++;
+}
+// Comment
+while ($i < 10);
--- /dev/null
+<?php
+
+if ($something) { echo 'hello';
+}
+
+if ($something) {
+ echo 'hello';
+} else { echo 'hi';
+}
+
+if ($something) {
+ echo 'hello';
+} else if ($else) { echo 'hi';
+}
+
+foreach ($something as $thing) { echo 'hello';
+}
+
+for ($i; $i > 0; $i--) { echo 'hello';
+}
+
+while ($something) { echo 'hello';
+}
+
+do {
+ $i--;
+} while ($something);
+
+if(true) {
+ $someObject->{$name};
+}
+
+if (true) :
+ $foo = true;
+endif;
+
+while (true) :
+ $foo = true;
+endwhile;
+
+for ($i; $i > 0; $i--) :
+ echo 'hello';
+endfor;
+
+foreach ($array as $element) :
+ echo 'hello';
+endforeach;
+
+while (!$this->readLine($tokens, $tag)) {}
+while (!$this->readLine($tokens, $tag)) {
+//skip to end of file
+}
+foreach ($cookies as $cookie) {
+ if ($cookie->match($uri, $matchSessionCookies, $now)) {
+ $ret[] = $cookie;
+ }
+}
+
+foreach ($stringParade as $hit) {
+ $hitParade[] = $hit + 0; //cast to integer
+}
+
+if ($foo) :
+ echo 'true';
+elseif ($something) :
+ echo 'foo';
+else:
+ echo 'false';
+endif;
+
+function test()
+{
+ if ($a) {
+ $a.=' '.($b ? 'b' : ($c ? ($d ? 'd' : 'c') : ''));
+ }
+}
+
+if ($a) {
+ foreach ($b as $c) {
+ if ($d) {
+ $e=$f;
+ $g=$h;
+ } elseif ($i==0) {
+ $j=$k;
+ }
+ }
+}
+
+?>
+<div style="text-align: right;">
+ <?php if ($model->scenario == 'simple') { $widget->renderPager(); } ?>
+</div>
+
+<?php
+switch ($this->error):
+ case Shop_Customer :: ERROR_INVALID_GENDER: ?>
+ Ungültiges Geschlecht!
+ <?php break;
+ case Shop_Customer :: ERROR_EMAIL_IN_USE: ?>
+ Die eingetragene E-Mail-Adresse ist bereits registriert.
+ <?php break;
+endswitch;
+
+if ($this->allowShopping !== true):
+ if ($this->status != Shop_Cart :: OK):
+ switch ($this->status):
+ case Shop_Cart :: NOT_FOUND:
+ echo 'foo';
+ endswitch;
+ endif;
+else:
+ echo 'foo';
+endif;
+
+// ELSE IF split over multiple lines (not inline)
+if ($test) {
+} else
+ if ($test) {
+ } else {
+ }
+
+switch($response = \Bar::baz('bat', function ($foo) {
+ return 'bar';
+})) {
+ case 1:
+ return 'test';
+
+ case 2:
+ return 'other';
+}
+
+$stuff = [1,2,3];
+foreach($stuff as $num) {
+ if ($num %2 ) {
+ echo "even";
+ } else {
+ echo "odd";
+ }
+}
+
+$i = 0;
+foreach($stuff as $num) {
+ do {
+ echo $i;
+ $i++;
+ } while ($i < 5);
+}
+
+foreach($stuff as $num) {
+ if (true) {
+ echo "true1\n";
+ }
+}
+ if (true) {
+ echo "true2\n";
+ }
+
+if ($foo) { echo 'foo';
+} elseif ($bar) { echo 'bar';
+} else { echo 'baz';
+}
+
+switch ($type) {
+ case 1:
+ if ($foo) {
+ return true;
+ } elseif ($baz) {
+ return true;
+ } else {
+ echo 'else';
+ }
+ break;
+}
+
+foreach ($sql as $s) {
+ if (!$this->execute) { echo "<pre>",$s.";\n</pre>";
+ } else {
+ $ok = $this->connDest->Execute($s);
+ if (!$ok) {
+ if ($this->neverAbort) { $ret = false;
+ } else { return false;
+ }
+ }
+ }
+}
+
+if ($bar) {
+ if ($foo) { echo 'hi'; // lol
+ }
+}
+
+if ($level == 'district') {
+ \DB::update(<<<EOD
+some
+text
+here
+EOD
+ );
+}
+
+if ($level == 'district') {
+ $var = <<<EOD
+some
+text
+here
+EOD;
+}
+
+if ($a && $a === Foo::VARIABLE && ($a === Foo::METHOD || $a === Foo::FUNCTION)) {
+ echo 'hi';
+}
+
+$out = array_map(function ($test) { if ($test) { return 1; } else { return 2;
+} }, $input); // comment
+
+for ($x=0;$x<5;$x++):
+ if ($x) { continue;
+ }
+endfor;
+
+for ($x=0;$x<5;$x++):
+ if ($x) { continue;
+ } ?> <?php
+endfor;
+
+if (true) {
+ try {
+ }
+ catch(Exception $e) {
+ }
+}
+
+do {
+ $i++;
+}
+// Comment
+while ($i < 10);
--- /dev/null
+
+
+if (something) print 'hello';
+
+if (something) {
+ print 'hello';
+} else print 'hi';
+
+if (something) {
+ print 'hello';
+} else if (something) print 'hi';
+
+for (i; i > 0; i--) print 'hello';
+
+while (something) print 'hello';
+
+do {
+ i--;
+} while (something);
+
+do i++; while (i < 5);
+
+SomeClass.prototype.switch = function() {
+ // do something
+};
+
+if ($("#myid").rotationDegrees()=='90')
+ $('.modal').css({'transform': 'rotate(90deg)'});
+
+if ($("#myid").rotationDegrees()=='90')
+ $foo = {'transform': 'rotate(90deg)'};
--- /dev/null
+
+
+if (something) { print 'hello';
+}
+
+if (something) {
+ print 'hello';
+} else { print 'hi';
+}
+
+if (something) {
+ print 'hello';
+} else if (something) { print 'hi';
+}
+
+for (i; i > 0; i--) { print 'hello';
+}
+
+while (something) { print 'hello';
+}
+
+do {
+ i--;
+} while (something);
+
+do { i++;
+} while (i < 5);
+
+SomeClass.prototype.switch = function() {
+ // do something
+};
+
+if ($("#myid").rotationDegrees()=='90') {
+ $('.modal').css({'transform': 'rotate(90deg)'});
+}
+
+if ($("#myid").rotationDegrees()=='90') {
+ $foo = {'transform': 'rotate(90deg)'};
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the InlineControlStructure 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\Standards\Generic\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class InlineControlStructureUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='InlineControlStructureUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'InlineControlStructureUnitTest.inc':
+ return array(
+ 3 => 1,
+ 7 => 1,
+ 11 => 1,
+ 13 => 1,
+ 15 => 1,
+ 17 => 1,
+ 23 => 1,
+ 42 => 1,
+ 43 => 1,
+ 45 => 1,
+ 46 => 1,
+ 49 => 1,
+ 62 => 1,
+ 66 => 1,
+ 78 => 1,
+ 120 => 1,
+ 128 => 1,
+ 134 => 1,
+ 142 => 1,
+ 143 => 1,
+ 144 => 1,
+ 150 => 1,
+ 158 => 1,
+ 159 => 1,
+ 162 => 1,
+ 163 => 1,
+ 164 => 1,
+ 167 => 1,
+ 168 => 1,
+ 170 => 1,
+ 178 => 1,
+ 185 => 1,
+ 188 => 2,
+ 191 => 1,
+ 195 => 1,
+ 198 => 1,
+ );
+ break;
+ case 'InlineControlStructureUnitTest.js':
+ return array(
+ 3 => 1,
+ 7 => 1,
+ 11 => 1,
+ 13 => 1,
+ 15 => 1,
+ 21 => 1,
+ 27 => 1,
+ 30 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+echo 'foo';
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the ByteOrderMark 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\Standards\Generic\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ByteOrderMarkUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(1 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+
+#login-container {}
+
--- /dev/null
+<?php
+echo 'foo';
+
--- /dev/null
+
+alert('hi);
+
--- /dev/null
+
+#login-container {}
--- /dev/null
+<?php
+echo 'foo';
--- /dev/null
+
+alert('hi);
--- /dev/null
+
+#login-container {}
\ No newline at end of file
--- /dev/null
+<?php
+echo 'foo';
\ No newline at end of file
--- /dev/null
+
+alert('hi);
\ No newline at end of file
--- /dev/null
+<?php
+/** Why me?
\ No newline at end of file
--- /dev/null
+<?php
+/** Why me?
--- /dev/null
+<?php
+/**
+ * Unit test class for the EndFileNewline 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\Standards\Generic\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class EndFileNewlineUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ switch ($testFile) {
+ case 'EndFileNewlineUnitTest.3.inc':
+ case 'EndFileNewlineUnitTest.3.js':
+ case 'EndFileNewlineUnitTest.3.css':
+ return array(2 => 1);
+ case 'EndFileNewlineUnitTest.4.inc':
+ // HHVM just removes the entire comment token, as if it was never there.
+ if (defined('HHVM_VERSION') === true) {
+ return array();
+ }
+ return array(2 => 1);
+ default:
+ return array();
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='')
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+
+#login-container {}
+
--- /dev/null
+<?php
+echo 'foo';
+
--- /dev/null
+
+alert('hi);
+
--- /dev/null
+
+#login-container {}
--- /dev/null
+<?php
+echo 'foo';
+?>
--- /dev/null
+
+alert('hi);
--- /dev/null
+
+#login-container {}
\ No newline at end of file
--- /dev/null
+<?php
+echo 'foo';
\ No newline at end of file
--- /dev/null
+
+alert('hi);
\ No newline at end of file
--- /dev/null
+<?php
+echo 'foo';
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/** Why me?
\ No newline at end of file
--- /dev/null
+<?php
+/** Why me?
--- /dev/null
+<?php
+/**
+ * Unit test class for the EndFileNoNewline 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\Standards\Generic\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class EndFileNoNewlineUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ switch ($testFile) {
+ case 'EndFileNoNewlineUnitTest.1.inc':
+ case 'EndFileNoNewlineUnitTest.1.css':
+ case 'EndFileNoNewlineUnitTest.1.js':
+ case 'EndFileNoNewlineUnitTest.2.inc':
+ return array(3 => 1);
+ case 'EndFileNoNewlineUnitTest.2.css':
+ case 'EndFileNoNewlineUnitTest.2.js':
+ return array(2 => 1);
+ case 'EndFileNoNewlineUnitTest.5.inc':
+ // HHVM just removes the entire comment token, as if it was never there.
+ if (defined('HHVM_VERSION') === true) {
+ return array(1 => 1);
+ }
+ return array();
+ case 'EndFileNoNewlineUnitTest.6.inc':
+ // HHVM just removes the entire comment token, as if it was never there.
+ if (defined('HHVM_VERSION') === true) {
+ return array(1 => 1);
+ }
+ return array(2 => 1);
+ default:
+ return array();
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='')
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+echo 'foo';
+
--- /dev/null
+<?php
+echo 'foo';
+?>
--- /dev/null
+<?php
+echo 'foo';
+?>
+<body>
+<?php
+echo 'bar';
--- /dev/null
+<html>
+<?php echo $allTheThings; ?>
+</html>
--- /dev/null
+#!/usr/bin/env php
+<?php
+echo 'foo';
--- /dev/null
+<?php
+/**
+ * Unit test class for the InlineHTML 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\Standards\Generic\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class InlineHTMLUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ switch ($testFile) {
+ case 'InlineHTMLUnitTest.3.inc':
+ return array(4 => 1);
+ break;
+ case 'InlineHTMLUnitTest.4.inc':
+ return array(1 => 1);
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='')
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+#login-container {
+ margin-left: -225px;
+}
--- /dev/null
+
+<?php
+echo 'hi';
+?>
+
+<?php
+echo 'hi';
+?>
+
+<?php
+function foo()
+{
+ $query->group('a.id,
+ uc.name,
+ ag.title,
+ ua.name'
+ );
+}
--- /dev/null
+
+<?php
+echo 'hi';
+?>
+
+<?php
+echo 'hi';
+?>
+
+<?php
+function foo()
+{
+ $query->group('a.id,
+ uc.name,
+ ag.title,
+ ua.name'
+ );
+}
--- /dev/null
+alert('hi');
+alert('hi');
--- /dev/null
+<?php
+/**
+ * Unit test class for the LineEndings 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\Standards\Generic\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LineEndingsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Get a list of CLI values to set before the file is tested.
+ *
+ * @param string $testFile The name of the file being tested.
+ * @param \PHP_CodeSniffer\Config $config The config data for the test run.
+ *
+ * @return void
+ */
+ public function setCliValues($testFile, $config)
+ {
+ $config->tabWidth = 4;
+
+ }//end setCliValues()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(1 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// This line is okay... just!
+if (($reallyLongVarName === true) || (is_array($anotherLongVarName) == false)) {
+ // Do something.
+}
+
+// This line is not okay... just!
+if (($reallyLongVarName === true) || (is_array($anotherLongVarName) === false)) {
+ // Do something.
+}
+
+
+// This line is is too long.
+if (($anotherReallyLongVarName === true) || (is_array($anotherReallyLongVarName) === false)) {
+ // Do something.
+}
+
+// This is a really really long comment that is going to go to exactly 80 chars.
+
+// This is another really really long comment that is going to go well over 80 characters.
+
+// And here is just a bunch of spaces that exceeds the line length.
+
+
+// And here are some spaces exactly 80 chars long.
+
+
+// This is a really really really really long long comment that is going to go to exactly 100 chars.
+
+// This is another really really really really really long comment that is going to go well over 100 characters.
+
+// And here is just a bunch of spaces that exceeds the max line length.
+
+
+// And here are some spaces exactly 100 chars long.
+
+?>
+<b>Hello</b>b>
+<?php
+echo 'hi';
+?>
+
+<?php
+// This is another really long comment that is going to go well over 100 characters, with no closing php tag after it.
+
+/**
+ * Does something cool, blah blah, not so long line.
+ *
+ * We're using Wilson scoring because blah blah, see:
+ * http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Wilson_score_interval
+ */
+
+// Go here:
+// http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Wilson_score_interval
+
+# http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Wilson_score_interval
+
+if ($foo) {
+ if ($bar) {
+ if ($baz) {
+ // Next line can be broken:
+ // foo bar baz http://en.wikipedia.org/wiki/Binomial_proportion#blahblahblahblah
+ // But this one is just too long to break with this indent:
+ // foo bar baz http://en.wikipedia.org/wiki/Binomial_proportion#blahblahblahblahblah
+ }
+ }
+}
+
+/* -------------------------------- 80 chars -------------------------------- */
+$ab = function () { /* comment */ if ($foo === true) { return (int) $foo; } };
+$ab = function () { /* comment */ if ($foo === true){ return (int)$foo; }};
+$ab = function () { /* comment */ if ($foo === true) { return (int) $foo; } };
+ $ab = function () { /* comment */ if ($foo === true) { return(int) $foo; }};
+ $ab = function () { /* comment */ if ($foo === true) { return(int) $foo; }};
+$ab = $ab = $ab = $ab = $ab = $ab = $ab = $ab = $ab = $ab;
--- /dev/null
+@codingStandardsChangeSetting Generic.Files.LineLength lineLimit 75
+@codingStandardsChangeSetting Generic.Files.LineLength absoluteLineLimit 80
+<?php
+
+/* This line is fine. This line is fine. */
+/* This line is too long. This line is too long. This line is too looooong. */
+/* This line is too long. This line is too long. This line is too long. This line is too long. */
--- /dev/null
+@codingStandardsChangeSetting Generic.Files.LineLength lineLimit 120
+@codingStandardsChangeSetting Generic.Files.LineLength absoluteLineLimit 150
+<?php
+
+/* This line is fine. This line is fine. This line is fine. This line is fine. This line is fine. */
+/* This line is too long. This line is too long. This line is too long. This line is too long. This line is too long. This line is too long. */
+/* This line is too long. This line is too long. This line is too long. This line is too long. This line is too long. This line is too long. This line is too long. */
--- /dev/null
+@codingStandardsChangeSetting Generic.Files.LineLength ignoreComments true
+@codingStandardsChangeSetting Generic.Files.LineLength lineLimit 80
+@codingStandardsChangeSetting Generic.Files.LineLength absoluteLineLimit 120
+<?php
+
+/* This line is fine. */
+if($thisIsOk === true) {}
+
+/* This line is too long but will be ignored. This line is too long but will be ignored. */
+if (($anotherReallyLongVarName === true) || (is_array($anotherReallyLongVarName) === false)) {
--- /dev/null
+<?php
+/**
+ * Unit test class for the LineLength 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\Standards\Generic\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LineLengthUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Get a list of CLI values to set before the file is tested.
+ *
+ * @param string $testFile The name of the file being tested.
+ * @param \PHP_CodeSniffer\Config $config The config data for the test run.
+ *
+ * @return void
+ */
+ public function setCliValues($testFile, $config)
+ {
+ $config->tabWidth = 4;
+
+ }//end setCliValues()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ switch ($testFile) {
+ case 'LineLengthUnitTest.1.inc':
+ return array(
+ 31 => 1,
+ 34 => 1,
+ 45 => 1,
+ );
+ break;
+ case 'LineLengthUnitTest.2.inc':
+ case 'LineLengthUnitTest.3.inc':
+ return array(7 => 1);
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='')
+ {
+ switch ($testFile) {
+ case 'LineLengthUnitTest.1.inc':
+ return array(
+ 9 => 1,
+ 15 => 1,
+ 21 => 1,
+ 24 => 1,
+ 29 => 1,
+ 37 => 1,
+ 63 => 1,
+ 73 => 1,
+ 75 => 1,
+ );
+ break;
+ case 'LineLengthUnitTest.2.inc':
+ case 'LineLengthUnitTest.3.inc':
+ return array(6 => 1);
+ break;
+ case 'LineLengthUnitTest.4.inc':
+ return array(10 => 1);
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+?>
+
+<?php
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the LowercasedFilename sniff.
+ *
+ * @author Andy Grunwald <andygrunwald@gmail.com>
+ * @copyright 2010-2014 Andy Grunwald
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LowercasedFilenameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(1 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class foo {
+
+}
+
+class bar {
+
+}
+
+class baz {
+
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the OneClassPerFile sniff.
+ *
+ * @author Andy Grunwald <andygrunwald@gmail.com>
+ * @copyright 2010-2014 Andy Grunwald
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class OneClassPerFileUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 6 => 1,
+ 10 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+interface foo {
+
+}
+
+interface bar {
+
+}
+
+interface baz {
+
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the OneInterfacePerFile sniff.
+ *
+ * @author Andy Grunwald <andygrunwald@gmail.com>
+ * @copyright 2010-2014 Andy Grunwald
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class OneInterfacePerFileUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 6 => 1,
+ 10 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class foo {
+
+}
+
+class bar {
+
+}
+
+class baz {
+
+}
+
+trait barTrait {
+
+}
+
+interface barInterface {
+
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the OneObjectStructurePerFile sniff.
+ *
+ * @author Mponos George <gmponos@gmail.com>
+ * @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\Standards\Generic\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class OneObjectStructurePerFileUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 6 => 1,
+ 10 => 1,
+ 14 => 1,
+ 18 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+trait foo {
+
+}
+
+trait bar {
+
+}
+
+trait baz {
+
+}
+
+$trait = 'foo';
+echo $this->trait;
+echo Something::$trait;
+echo $trait;
--- /dev/null
+<?php
+/**
+ * Unit test class for the OneTraitPerFile sniff.
+ *
+ * @author Alexander Obuhovich <aik.bold@gmail.com>
+ * @copyright 2010-2014 Alexander Obuhovich
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class OneTraitPerFileUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Should this test be skipped for some reason.
+ *
+ * @return bool
+ */
+ protected function shouldSkipTest()
+ {
+ return (PHP_VERSION_ID < 50400);
+
+ }//end shouldSkipTest()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array(int => int)
+ */
+ public function getErrorList()
+ {
+ return array(
+ 6 => 1,
+ 10 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array(int => int)
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$y = 2;;
+echo $y;
+for ($i = 1; $i < $length; $i++) {}
+for (; $i < $length; $i++) {}
+echo 'x'; echo $y;
+$x = 10; echo $y;
+$this->wizardid = 10; $this->paint(); echo 'x';
+?>
+<div class="<?php echo $class; ?>" id="<?php echo $id; ?>"></div>
+<div class="<?php echo $class ?>" id="<?php echo $id ?>"></div>
+<div class="<?= $class; ?>" id="<?= $id; ?>"></div>
+<div class="<?= $class ?>" id="<?= $id ?>"></div>
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowMultipleStatements 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\Standards\Generic\Tests\Formatting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowMultipleStatementsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 6 => 1,
+ 7 => 1,
+ 8 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// Valid
+$var1 = 'var1';
+$var10 = 'var1';
+$var100 = 'var1';
+$var1000 = 'var1';
+
+// Invalid
+$var1 = 'var1';
+$var10 = 'var1';
+$var100 = 'var1';
+$var1000 = 'var1';
+
+// Valid
+$var1 = 'var1';
+$var10 = 'var1';
+
+$var100 = 'var1';
+$var1000 = 'var1';
+
+// Invalid
+$var1 = 'var1';
+$var10 = 'var1';
+
+$var100 = 'var1';
+$var1000 = 'var1';
+
+// Valid
+$var1 .= 'var1';
+$var10 .= 'var1';
+$var100 .= 'var1';
+$var1000 .= 'var1';
+
+// Invalid
+$var1 .= 'var1';
+$var10.= 'var1';
+$var100 .= 'var1';
+$var1000 .= 'var1';
+
+// Valid
+$var1 = 'var1';
+$var10 .= 'var1';
+$var100 = 'var1';
+$var1000 .= 'var1';
+
+// Invalid
+$var1 = 'var1';
+$var10 .= 'var1';
+$var100 = 'var1';
+$var1000.= 'var1';
+
+// Valid
+$var1 .= 'var1';
+$var10 .= 'var1';
+
+$var100 .= 'var1';
+$var1000 .= 'var1';
+
+// Invalid
+$var1 .= 'var1';
+$var10 .= 'var1';
+
+$var100 .= 'var1';
+$var1000 .= 'var1';
+
+// Valid
+$var = 100;
+
+// InValid
+$var = 100;
+
+$commentStart = $phpcsFile->findPrevious();
+$commentEnd = $this->_phpcsFile;
+$expected .= '...';
+
+// Invalid
+$this->okButton = new Button();
+$content = new MyClass();
+
+
+$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
+
+class MyClass
+{
+ const MODE_DEBUG = 'debug';
+ const MODE_DEBUG2 = 'debug';
+
+ $array[$test] = 'anything';
+ $var = 'anything';
+
+ const MODE_DEBUG2 = 'debug';
+ $array[$test] = 'anything';
+ $var = 'anything';
+ $array[($test + 1)] = 'anything';
+ $array[($blah + (10 - $test))] = 'anything';
+}
+
+function myFunction($var=true)
+{
+ if ($strict === true) {
+ $length = strlen($string);
+ $lastCharWasCaps = ($classFormat === false) ? false : true;
+
+ for ($i = 1; $i < $length; $i++) {
+ $isCaps = (strtoupper($string{$i}) === $string{$i}) ? true : false;
+ if ($isCaps === true && $lastCharWasCaps === true) {
+ return false;
+ }
+
+ $lastCharWasCaps = $isCaps;
+ }
+ }
+}
+
+// Valid
+for ($i = 0; $i < 10; $i += 2) {
+ $i = ($i - 1);
+}
+
+// Invalid
+foreach ($files as $file) {
+ $saves[$file] = array();
+ $contents = stripslashes(file_get_contents($file));
+ list($assetid, $time, $content) = explode("\n", $contents);
+ $saves[$file]['assetid'] = $assetid;
+}
+
+$i = ($i - 10);
+$ip = ($i - 10);
+for ($i = 0; $i < 10; $i += 2) {
+ $i = ($i - 10);
+}
+
+// Valid
+$variable = 12;
+$var = a_very(long_line('that', 'contains'),
+ a_bunch('of long', 'parameters'),
+ 'that_need to be aligned with the equal sign');
+$var2 = 12;
+
+// Valid
+$variable = 12;
+$var = 'a very long line of text that contains '
+ .$someVar
+ .' and some other stuff that is too long to fit on one line';
+$var2 = 12;
+
+// Invalid
+$variable = 12;
+$var = a_very(long_line('that', 'contains'),
+ a_bunch('of long', 'parameters'),
+ 'that_need to be aligned with the equal sign');
+$var2 = 12;
+
+// Invalid
+$variable = 12;
+$var = 'a very long line of text that contains '
+ .$someVar
+ .' and some other stuff that is too long to fit on one line';
+$var2 = 12;
+
+// Valid
+$variable = 12;
+$var .= 'a very long line of text that contains '
+ .$someVar
+ .' and some other stuff that is too long to fit on one line';
+$var2 = 12;
+
+// Valid
+$variable += 12;
+$var .= 'a very long line of text that contains '
+ .$someVar
+ .' and some other stuff that is too long to fit on one line';
+$var2 = 12;
+
+// Invalid
+$variable = 12;
+$var .= 'a very long line of text that contains '
+ .$someVar
+ .' and some other stuff that is too long to fit on one line';
+$var2 = 12;
+
+// Valid
+$error = false;
+while (list($h, $f) = each($handle)) {
+ $error = true;
+}
+
+// Valid
+$value = false;
+function blah ($value = true) {
+ $value = false;
+ if ($value === true) {
+ $value = false;
+ }
+}
+
+if (TRUE) {
+ $args = array('foo' => 'foo');
+ $res = 'bar';
+}
+
+// @codingStandardsChangeSetting Generic.Formatting.MultipleStatementAlignment maxPadding 8
+
+$one = 'one';
+$varonetwo = 'two';
+$varonetwothree = 'three';
+$varonetwothreefour = 'four';
+
+$one = 'one';
+$varonetwo .= 'two';
+$varonetwo = 'two';
+$varonetwo .= 'two';
+$varonetwothree = 'three';
+$varonetwothreefour = 'four';
+
+// @codingStandardsChangeSetting Generic.Formatting.MultipleStatementAlignment maxPadding 1000
+
+$filterQuery = SquizRoadmap::getSearchQueryFilter($searchParams);
+Channels::addToBasket('itemid', $filterQuery);
+$query = DAL::getDALQuery('SquizRoadmapItem', 'getItemIds');
+$results = DAL::getAssoc($query, 0);
+
+$path = BaseSystem::getDataDir('SquizRoadmap').'/items/';
+$path .= FileSystem::getHashDir($itemid).'/'.$itemid;
+
+$contents .= 'if (';
+$conditions = array();
+
+$bar = 'hi';
+$foo = $moo = $test;
+$boo = 'boo';
+$foo = $moooo = 'foo';
--- /dev/null
+<?php
+
+// Valid
+$var1 = 'var1';
+$var10 = 'var1';
+$var100 = 'var1';
+$var1000 = 'var1';
+
+// Invalid
+$var1 = 'var1';
+$var10 = 'var1';
+$var100 = 'var1';
+$var1000 = 'var1';
+
+// Valid
+$var1 = 'var1';
+$var10 = 'var1';
+
+$var100 = 'var1';
+$var1000 = 'var1';
+
+// Invalid
+$var1 = 'var1';
+$var10 = 'var1';
+
+$var100 = 'var1';
+$var1000 = 'var1';
+
+// Valid
+$var1 .= 'var1';
+$var10 .= 'var1';
+$var100 .= 'var1';
+$var1000 .= 'var1';
+
+// Invalid
+$var1 .= 'var1';
+$var10 .= 'var1';
+$var100 .= 'var1';
+$var1000 .= 'var1';
+
+// Valid
+$var1 = 'var1';
+$var10 .= 'var1';
+$var100 = 'var1';
+$var1000 .= 'var1';
+
+// Invalid
+$var1 = 'var1';
+$var10 .= 'var1';
+$var100 = 'var1';
+$var1000 .= 'var1';
+
+// Valid
+$var1 .= 'var1';
+$var10 .= 'var1';
+
+$var100 .= 'var1';
+$var1000 .= 'var1';
+
+// Invalid
+$var1 .= 'var1';
+$var10 .= 'var1';
+
+$var100 .= 'var1';
+$var1000 .= 'var1';
+
+// Valid
+$var = 100;
+
+// InValid
+$var = 100;
+
+$commentStart = $phpcsFile->findPrevious();
+$commentEnd = $this->_phpcsFile;
+$expected .= '...';
+
+// Invalid
+$this->okButton = new Button();
+$content = new MyClass();
+
+
+$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
+
+class MyClass
+{
+ const MODE_DEBUG = 'debug';
+ const MODE_DEBUG2 = 'debug';
+
+ $array[$test] = 'anything';
+ $var = 'anything';
+
+ const MODE_DEBUG2 = 'debug';
+ $array[$test] = 'anything';
+ $var = 'anything';
+ $array[($test + 1)] = 'anything';
+ $array[($blah + (10 - $test))] = 'anything';
+}
+
+function myFunction($var=true)
+{
+ if ($strict === true) {
+ $length = strlen($string);
+ $lastCharWasCaps = ($classFormat === false) ? false : true;
+
+ for ($i = 1; $i < $length; $i++) {
+ $isCaps = (strtoupper($string{$i}) === $string{$i}) ? true : false;
+ if ($isCaps === true && $lastCharWasCaps === true) {
+ return false;
+ }
+
+ $lastCharWasCaps = $isCaps;
+ }
+ }
+}
+
+// Valid
+for ($i = 0; $i < 10; $i += 2) {
+ $i = ($i - 1);
+}
+
+// Invalid
+foreach ($files as $file) {
+ $saves[$file] = array();
+ $contents = stripslashes(file_get_contents($file));
+ list($assetid, $time, $content) = explode("\n", $contents);
+ $saves[$file]['assetid'] = $assetid;
+}
+
+$i = ($i - 10);
+$ip = ($i - 10);
+for ($i = 0; $i < 10; $i += 2) {
+ $i = ($i - 10);
+}
+
+// Valid
+$variable = 12;
+$var = a_very(long_line('that', 'contains'),
+ a_bunch('of long', 'parameters'),
+ 'that_need to be aligned with the equal sign');
+$var2 = 12;
+
+// Valid
+$variable = 12;
+$var = 'a very long line of text that contains '
+ .$someVar
+ .' and some other stuff that is too long to fit on one line';
+$var2 = 12;
+
+// Invalid
+$variable = 12;
+$var = a_very(long_line('that', 'contains'),
+ a_bunch('of long', 'parameters'),
+ 'that_need to be aligned with the equal sign');
+$var2 = 12;
+
+// Invalid
+$variable = 12;
+$var = 'a very long line of text that contains '
+ .$someVar
+ .' and some other stuff that is too long to fit on one line';
+$var2 = 12;
+
+// Valid
+$variable = 12;
+$var .= 'a very long line of text that contains '
+ .$someVar
+ .' and some other stuff that is too long to fit on one line';
+$var2 = 12;
+
+// Valid
+$variable += 12;
+$var .= 'a very long line of text that contains '
+ .$someVar
+ .' and some other stuff that is too long to fit on one line';
+$var2 = 12;
+
+// Invalid
+$variable = 12;
+$var .= 'a very long line of text that contains '
+ .$someVar
+ .' and some other stuff that is too long to fit on one line';
+$var2 = 12;
+
+// Valid
+$error = false;
+while (list($h, $f) = each($handle)) {
+ $error = true;
+}
+
+// Valid
+$value = false;
+function blah ($value = true) {
+ $value = false;
+ if ($value === true) {
+ $value = false;
+ }
+}
+
+if (TRUE) {
+ $args = array('foo' => 'foo');
+ $res = 'bar';
+}
+
+// @codingStandardsChangeSetting Generic.Formatting.MultipleStatementAlignment maxPadding 8
+
+$one = 'one';
+$varonetwo = 'two';
+$varonetwothree = 'three';
+$varonetwothreefour = 'four';
+
+$one = 'one';
+$varonetwo .= 'two';
+$varonetwo = 'two';
+$varonetwo .= 'two';
+$varonetwothree = 'three';
+$varonetwothreefour = 'four';
+
+// @codingStandardsChangeSetting Generic.Formatting.MultipleStatementAlignment maxPadding 1000
+
+$filterQuery = SquizRoadmap::getSearchQueryFilter($searchParams);
+Channels::addToBasket('itemid', $filterQuery);
+$query = DAL::getDALQuery('SquizRoadmapItem', 'getItemIds');
+$results = DAL::getAssoc($query, 0);
+
+$path = BaseSystem::getDataDir('SquizRoadmap').'/items/';
+$path .= FileSystem::getHashDir($itemid).'/'.$itemid;
+
+$contents .= 'if (';
+$conditions = array();
+
+$bar = 'hi';
+$foo = $moo = $test;
+$boo = 'boo';
+$foo = $moooo = 'foo';
--- /dev/null
+
+
+// Valid
+var1 = 'var1';
+var10 = 'var1';
+var100 = 'var1';
+var1000 = 'var1';
+
+// Invalid
+var1 = 'var1';
+var10 = 'var1';
+var100 = 'var1';
+var1000 = 'var1';
+
+// Valid
+var1 = 'var1';
+var10 = 'var1';
+
+var100 = 'var1';
+var1000 = 'var1';
+
+// Invalid
+var1 = 'var1';
+var10 = 'var1';
+
+var100 = 'var1';
+var1000 = 'var1';
+
+// Valid
+var1 += 'var1';
+var10 += 'var1';
+var100 += 'var1';
+var1000 += 'var1';
+
+// Invalid
+var1 += 'var1';
+var10+= 'var1';
+var100 += 'var1';
+var1000 += 'var1';
+
+// Valid
+var1 = 'var1';
+var10 += 'var1';
+var100 = 'var1';
+var1000 += 'var1';
+
+// Invalid
+var1 = 'var1';
+var10 += 'var1';
+var100 = 'var1';
+var1000+= 'var1';
+
+// Valid
+var1 += 'var1';
+var10 += 'var1';
+
+var100 += 'var1';
+var1000 += 'var1';
+
+// Invalid
+var1 += 'var1';
+var10 += 'var1';
+
+var100 += 'var1';
+var1000 += 'var1';
+
+// Valid
+var test = 100;
+
+// InValid
+var test = 100;
+
+commentStart = phpcsFile.findPrevious();
+commentEnd = this._phpcsFile;
+expected += '...';
+
+// Invalid
+this.okButton = {};
+content = {};
+
+var buttonid = [this.id, '-positionFormats-add'].join('');
+var buttonWidget = WidgetStore.get(buttonid);
+var spinButtonid = [this.id, '-positionFormats-spinButton'].join('');
+var spinButtonWidget = WidgetStore.get(spinButtonid);
+var position = spinButtonWidget.getValue();
+var posForamatsList = WidgetStore.get([self.id, '-positionFormats-list'].join(''));
+
+dfx.stripTags = function(content, allowedTags)
+{
+ var match;
+ var re = 'blah';
+};
+
+var contents += 'if (';
+var conditions = array();
+
+var foo = {};
+foo.blah = 'blah';
+
+var script = document.createElement('script');
+script.onload = function()
+{
+ clearTimeout(t);
+};
+
+stream.match(stream.sol() ? /^\s*\/\/.*/ : /^\s+\/\/.*/);
+function() {
+ if (condition)
+ foo = .4
+}
--- /dev/null
+
+
+// Valid
+var1 = 'var1';
+var10 = 'var1';
+var100 = 'var1';
+var1000 = 'var1';
+
+// Invalid
+var1 = 'var1';
+var10 = 'var1';
+var100 = 'var1';
+var1000 = 'var1';
+
+// Valid
+var1 = 'var1';
+var10 = 'var1';
+
+var100 = 'var1';
+var1000 = 'var1';
+
+// Invalid
+var1 = 'var1';
+var10 = 'var1';
+
+var100 = 'var1';
+var1000 = 'var1';
+
+// Valid
+var1 += 'var1';
+var10 += 'var1';
+var100 += 'var1';
+var1000 += 'var1';
+
+// Invalid
+var1 += 'var1';
+var10 += 'var1';
+var100 += 'var1';
+var1000 += 'var1';
+
+// Valid
+var1 = 'var1';
+var10 += 'var1';
+var100 = 'var1';
+var1000 += 'var1';
+
+// Invalid
+var1 = 'var1';
+var10 += 'var1';
+var100 = 'var1';
+var1000 += 'var1';
+
+// Valid
+var1 += 'var1';
+var10 += 'var1';
+
+var100 += 'var1';
+var1000 += 'var1';
+
+// Invalid
+var1 += 'var1';
+var10 += 'var1';
+
+var100 += 'var1';
+var1000 += 'var1';
+
+// Valid
+var test = 100;
+
+// InValid
+var test = 100;
+
+commentStart = phpcsFile.findPrevious();
+commentEnd = this._phpcsFile;
+expected += '...';
+
+// Invalid
+this.okButton = {};
+content = {};
+
+var buttonid = [this.id, '-positionFormats-add'].join('');
+var buttonWidget = WidgetStore.get(buttonid);
+var spinButtonid = [this.id, '-positionFormats-spinButton'].join('');
+var spinButtonWidget = WidgetStore.get(spinButtonid);
+var position = spinButtonWidget.getValue();
+var posForamatsList = WidgetStore.get([self.id, '-positionFormats-list'].join(''));
+
+dfx.stripTags = function(content, allowedTags)
+{
+ var match;
+ var re = 'blah';
+};
+
+var contents += 'if (';
+var conditions = array();
+
+var foo = {};
+foo.blah = 'blah';
+
+var script = document.createElement('script');
+script.onload = function()
+{
+ clearTimeout(t);
+};
+
+stream.match(stream.sol() ? /^\s*\/\/.*/ : /^\s+\/\/.*/);
+function() {
+ if (condition)
+ foo = .4
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the MultipleStatementAlignment 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\Standards\Generic\Tests\Formatting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class MultipleStatementAlignmentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='MultipleStatementAlignmentUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'MultipleStatementAlignmentUnitTest.inc':
+ return array(
+ 11 => 1,
+ 12 => 1,
+ 23 => 1,
+ 24 => 1,
+ 26 => 1,
+ 27 => 1,
+ 37 => 1,
+ 38 => 1,
+ 48 => 1,
+ 50 => 1,
+ 51 => 1,
+ 61 => 1,
+ 62 => 1,
+ 64 => 1,
+ 65 => 1,
+ 71 => 1,
+ 78 => 1,
+ 79 => 1,
+ 86 => 1,
+ 92 => 1,
+ 93 => 1,
+ 94 => 1,
+ 95 => 1,
+ 123 => 1,
+ 124 => 1,
+ 126 => 1,
+ 129 => 1,
+ 154 => 1,
+ 161 => 1,
+ 178 => 1,
+ 179 => 1,
+ 182 => 1,
+ 206 => 1,
+ 207 => 1,
+ );
+ break;
+ case 'MultipleStatementAlignmentUnitTest.js':
+ return array(
+ 11 => 1,
+ 12 => 1,
+ 23 => 1,
+ 24 => 1,
+ 26 => 1,
+ 27 => 1,
+ 37 => 1,
+ 38 => 1,
+ 48 => 1,
+ 50 => 1,
+ 51 => 1,
+ 61 => 1,
+ 62 => 1,
+ 64 => 1,
+ 65 => 1,
+ 71 => 1,
+ 78 => 1,
+ 79 => 1,
+ 81 => 1,
+ 82 => 1,
+ 83 => 1,
+ 85 => 1,
+ 86 => 1,
+ 100 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+$var = (int) $var2;
+$var = (int)$var2;
+$var = (int) $var2;
+
+$var = (integer) $var2;
+$var = (integer)$var2;
+$var = (integer) $var2;
+
+$var = (string) $var2;
+$var = (string)$var2;
+$var = (string) $var2;
+
+$var = (float) $var2;
+$var = (float)$var2;
+$var = (float) $var2;
+
+$var = (double) $var2;
+$var = (double)$var2;
+$var = (double) $var2;
+
+$var = (real) $var2;
+$var = (real)$var2;
+$var = (real) $var2;
+
+$var = (array) $var2;
+$var = (array)$var2;
+$var = (array) $var2;
+
+$var = (bool) $var2;
+$var = (bool)$var2;
+$var = (bool) $var2;
+
+$var = (boolean) $var2;
+$var = (boolean)$var2;
+$var = (boolean) $var2;
+
+$var = (object) $var2;
+$var = (object)$var2;
+$var = (object) $var2;
+
+$var = (unset) $var2;
+$var = (unset)$var2;
+$var = (unset) $var2;
--- /dev/null
+<?php
+
+$var = (int)$var2;
+$var = (int)$var2;
+$var = (int)$var2;
+
+$var = (integer)$var2;
+$var = (integer)$var2;
+$var = (integer)$var2;
+
+$var = (string)$var2;
+$var = (string)$var2;
+$var = (string)$var2;
+
+$var = (float)$var2;
+$var = (float)$var2;
+$var = (float)$var2;
+
+$var = (double)$var2;
+$var = (double)$var2;
+$var = (double)$var2;
+
+$var = (real)$var2;
+$var = (real)$var2;
+$var = (real)$var2;
+
+$var = (array)$var2;
+$var = (array)$var2;
+$var = (array)$var2;
+
+$var = (bool)$var2;
+$var = (bool)$var2;
+$var = (bool)$var2;
+
+$var = (boolean)$var2;
+$var = (boolean)$var2;
+$var = (boolean)$var2;
+
+$var = (object)$var2;
+$var = (object)$var2;
+$var = (object)$var2;
+
+$var = (unset)$var2;
+$var = (unset)$var2;
+$var = (unset)$var2;
--- /dev/null
+<?php
+/**
+ * Unit test class for the NoSpaceAfterCast 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\Standards\Generic\Tests\Formatting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class NoSpaceAfterCastUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 5 => 1,
+ 7 => 1,
+ 9 => 1,
+ 11 => 1,
+ 13 => 1,
+ 15 => 1,
+ 17 => 1,
+ 19 => 1,
+ 21 => 1,
+ 23 => 1,
+ 25 => 1,
+ 27 => 1,
+ 29 => 1,
+ 31 => 1,
+ 33 => 1,
+ 35 => 1,
+ 37 => 1,
+ 39 => 1,
+ 41 => 1,
+ 43 => 1,
+ 45 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+$var = (int) $var2;
+$var = (int)$var2;
+$var = (int) $var2;
+
+$var = (integer) $var2;
+$var = (integer)$var2;
+$var = (integer) $var2;
+
+$var = (string) $var2;
+$var = (string)$var2;
+$var = (string) $var2;
+
+$var = (float) $var2;
+$var = (float)$var2;
+$var = (float) $var2;
+
+$var = (double) $var2;
+$var = (double)$var2;
+$var = (double) $var2;
+
+$var = (real) $var2;
+$var = (real)$var2;
+$var = (real) $var2;
+
+$var = (array) $var2;
+$var = (array)$var2;
+$var = (array) $var2;
+
+$var = (bool) $var2;
+$var = (bool)$var2;
+$var = (bool) $var2;
+
+$var = (boolean) $var2;
+$var = (boolean)$var2;
+$var = (boolean) $var2;
+
+$var = (object) $var2;
+$var = (object)$var2;
+$var = (object) $var2;
+
+$var = (unset) $var2;
+$var = (unset)$var2;
+$var = (unset) $var2;
--- /dev/null
+<?php
+
+$var = (int) $var2;
+$var = (int) $var2;
+$var = (int) $var2;
+
+$var = (integer) $var2;
+$var = (integer) $var2;
+$var = (integer) $var2;
+
+$var = (string) $var2;
+$var = (string) $var2;
+$var = (string) $var2;
+
+$var = (float) $var2;
+$var = (float) $var2;
+$var = (float) $var2;
+
+$var = (double) $var2;
+$var = (double) $var2;
+$var = (double) $var2;
+
+$var = (real) $var2;
+$var = (real) $var2;
+$var = (real) $var2;
+
+$var = (array) $var2;
+$var = (array) $var2;
+$var = (array) $var2;
+
+$var = (bool) $var2;
+$var = (bool) $var2;
+$var = (bool) $var2;
+
+$var = (boolean) $var2;
+$var = (boolean) $var2;
+$var = (boolean) $var2;
+
+$var = (object) $var2;
+$var = (object) $var2;
+$var = (object) $var2;
+
+$var = (unset) $var2;
+$var = (unset) $var2;
+$var = (unset) $var2;
--- /dev/null
+<?php
+/**
+ * Unit test class for the SpaceAfterCast 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\Standards\Generic\Tests\Formatting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class SpaceAfterCastUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 4 => 1,
+ 5 => 1,
+ 8 => 1,
+ 9 => 1,
+ 12 => 1,
+ 13 => 1,
+ 16 => 1,
+ 17 => 1,
+ 20 => 1,
+ 21 => 1,
+ 24 => 1,
+ 25 => 1,
+ 28 => 1,
+ 29 => 1,
+ 32 => 1,
+ 33 => 1,
+ 36 => 1,
+ 37 => 1,
+ 40 => 1,
+ 41 => 1,
+ 44 => 1,
+ 45 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+if (!$someVar || !$x instanceOf 'stdClass') {}
+if (! $someVar || ! $x instanceOf 'stdClass') {}
+if (!foo() && (!$x || true)) {}
+$var = !($x || $y);
--- /dev/null
+<?php
+if (! $someVar || ! $x instanceOf 'stdClass') {}
+if (! $someVar || ! $x instanceOf 'stdClass') {}
+if (! foo() && (! $x || true)) {}
+$var = ! ($x || $y);
--- /dev/null
+<?php
+if (!someVar || !x) {}
+if (! someVar || ! x) {}
+if (!foo() && (!x || true)) {}
+var z = !(x || y);
--- /dev/null
+<?php
+if (! someVar || ! x) {}
+if (! someVar || ! x) {}
+if (! foo() && (! x || true)) {}
+var z = ! (x || y);
--- /dev/null
+<?php
+/**
+ * Unit test class for the SpaceAfterNot 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\Standards\Generic\Tests\Formatting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class SpaceAfterNotUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 2,
+ 4 => 2,
+ 5 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class myclass extends yourclass implements someint {
+ function myfunc($var) {
+ echo $var;
+ }
+}
+
+$myvar = true;
+myfunc(&$myvar);
+myfunc($myvar);
+
+$this->myfunc(&$myvar);
+$this->myfunc($myvar);
+
+myclass::myfunc(&$myvar);
+myclass::myfunc($myvar);
+
+while(testfunc($var1, &$var2, $var3, &$var4) === false) {
+}
+
+sprintf("0%o", 0777 & $p);
+
+$foo(&$myvar);
+
+if (is_array($foo = &$this->bar())) {
+}
+
+Hooks::run( 'SecondaryDataUpdates', [ $title, $old, $recursive, $parserOutput, &$updates ] );
+
+$foo = Bar(&$fooBar);
+
+myfunc($myvar&$myvar);
+myfunc($myvar[0]&$myvar);
--- /dev/null
+<?php
+/**
+ * Unit test class for the CallTimePassByReference 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\Standards\Generic\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class CallTimePassByReferenceUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 9 => 1,
+ 12 => 1,
+ 15 => 1,
+ 18 => 2,
+ 23 => 1,
+ 30 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+$result = myFunction();
+$result = myFunction($arg1, $arg2);
+$result = myFunction($arg1,$arg2);
+$result = myFunction($arg1 , $arg2);
+$result = myFunction($arg1 , $arg2);
+$result = myFunction($arg1, $arg2, $arg3,$arg4, $arg5);
+$result = myFunction($arg1, $arg2, $arg3, $arg4, $arg5);
+$result = myFunction($arg1, $arg2 = array());
+$result = myFunction($arg1 , $arg2 =array());
+$result = myFunction($arg1 , $arg2= array());
+$result = myFunction($arg1 , $arg2=array());
+
+$result = myFunction($arg1,
+ $arg2 = array(),
+ $arg3,
+ $arg4,
+ $arg5);
+
+throw new Exception("This is some massive string for a message",
+ $cause);
+
+// Function definitions are ignored
+function myFunction($arg1,$arg2)
+{
+}
+
+function myFunction ($arg1,$arg2)
+{
+}
+
+function myFunction($arg1=1,$arg2=2)
+{
+}
+
+
+function myFunction($arg1 = 1,$arg2 = 2)
+{
+}
+
+$key = array_search($this->getArray($one, $two,$three),$this->arrayMap);
+$this->error($obj->getCode(),$obj->getMessage(),$obj->getFile(),$obj->getLine());
+
+make_foo($string /*the string*/ , true /*test*/);
+make_foo($string/*the string*/ , /*test*/ true);
+make_foo($string /*the string*/, /*test*/ true);
+
+class MyClass {
+ function myFunction() {
+ blah($foo, "{{$config['host']}}", "{$config}", "hi there{}{}{{{}{}{}}");
+ }
+}
+
+// Function definition, not function call, so should be ignored
+function &myFunction($arg1=1,$arg2=2)
+{
+}
+
+return array_udiff(
+ $foo,
+ $bar,
+ function($a, $b) {
+ $foo='bar';
+ return $foo;
+ }
+);
+
+var_dump(<<<FOO
+foo
+FOO
+,
+<<<BAR
+bar
+BAR
+, <<<BAZ
+baz
+BAZ
+,<<<'NOW'
+now
+NOW
+, <<<'THEN'
+then
+THEN
+);
+
+if (in_array($arg1, ['foo','bar'])) {}
+if (in_array($arg1, array('foo','bar'))) {}
+
+$b = foo(
+ "1", // this is a comment
+ "2", // this is a comment
+ "3",// this is a comment
+ "4"
+);
+
+var_dump(
+ <<<TEXT
+foo
+TEXT
+ ,
+ 'bar'
+);
+
+unset($foo,$bar);
+
+$closure($foo,$bar);
+$var = $closure() + $closure($foo,$bar) + self::$closure($foo,$bar);
+
+class Test
+{
+ public static function baz($foo, $bar)
+ {
+ $a = new self($foo,$bar);
+ $b = new static($foo,$bar);
+ }
+}
+
+$obj->{$var}($foo,$bar);
+
+(function ($a, $b) {
+ return function ($c, $d) use ($a, $b) {
+ echo $a, $b, $c, $d;
+ };
+})('a','b')('c','d');
--- /dev/null
+<?php
+
+$result = myFunction();
+$result = myFunction($arg1, $arg2);
+$result = myFunction($arg1, $arg2);
+$result = myFunction($arg1, $arg2);
+$result = myFunction($arg1, $arg2);
+$result = myFunction($arg1, $arg2, $arg3, $arg4, $arg5);
+$result = myFunction($arg1, $arg2, $arg3, $arg4, $arg5);
+$result = myFunction($arg1, $arg2 = array());
+$result = myFunction($arg1, $arg2 = array());
+$result = myFunction($arg1, $arg2 = array());
+$result = myFunction($arg1, $arg2 = array());
+
+$result = myFunction($arg1,
+ $arg2 = array(),
+ $arg3,
+ $arg4,
+ $arg5);
+
+throw new Exception("This is some massive string for a message",
+ $cause);
+
+// Function definitions are ignored
+function myFunction($arg1,$arg2)
+{
+}
+
+function myFunction ($arg1,$arg2)
+{
+}
+
+function myFunction($arg1=1,$arg2=2)
+{
+}
+
+
+function myFunction($arg1 = 1,$arg2 = 2)
+{
+}
+
+$key = array_search($this->getArray($one, $two, $three), $this->arrayMap);
+$this->error($obj->getCode(), $obj->getMessage(), $obj->getFile(), $obj->getLine());
+
+make_foo($string /*the string*/, true /*test*/);
+make_foo($string/*the string*/, /*test*/ true);
+make_foo($string /*the string*/, /*test*/ true);
+
+class MyClass {
+ function myFunction() {
+ blah($foo, "{{$config['host']}}", "{$config}", "hi there{}{}{{{}{}{}}");
+ }
+}
+
+// Function definition, not function call, so should be ignored
+function &myFunction($arg1=1,$arg2=2)
+{
+}
+
+return array_udiff(
+ $foo,
+ $bar,
+ function($a, $b) {
+ $foo='bar';
+ return $foo;
+ }
+);
+
+var_dump(<<<FOO
+foo
+FOO
+,
+<<<BAR
+bar
+BAR
+, <<<BAZ
+baz
+BAZ
+, <<<'NOW'
+now
+NOW
+, <<<'THEN'
+then
+THEN
+);
+
+if (in_array($arg1, ['foo','bar'])) {}
+if (in_array($arg1, array('foo','bar'))) {}
+
+$b = foo(
+ "1", // this is a comment
+ "2", // this is a comment
+ "3", // this is a comment
+ "4"
+);
+
+var_dump(
+ <<<TEXT
+foo
+TEXT
+ ,
+ 'bar'
+);
+
+unset($foo, $bar);
+
+$closure($foo, $bar);
+$var = $closure() + $closure($foo, $bar) + self::$closure($foo, $bar);
+
+class Test
+{
+ public static function baz($foo, $bar)
+ {
+ $a = new self($foo, $bar);
+ $b = new static($foo, $bar);
+ }
+}
+
+$obj->{$var}($foo, $bar);
+
+(function ($a, $b) {
+ return function ($c, $d) use ($a, $b) {
+ echo $a, $b, $c, $d;
+ };
+})('a', 'b')('c', 'd');
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionCallArgumentSpacing 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\Standards\Generic\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionCallArgumentSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 5 => 1,
+ 6 => 1,
+ 7 => 2,
+ 8 => 1,
+ 11 => 2,
+ 12 => 2,
+ 13 => 3,
+ 42 => 3,
+ 43 => 3,
+ 45 => 1,
+ 46 => 2,
+ 79 => 1,
+ 82 => 1,
+ 93 => 1,
+ 105 => 1,
+ 107 => 1,
+ 108 => 2,
+ 114 => 1,
+ 115 => 1,
+ 119 => 1,
+ 125 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// Brace should be on new line.
+function myFunction() {
+}
+
+// Good.
+function myFunction()
+{
+}
+
+// Too many spaces.
+function myFunction() {
+}
+
+// Too many newlines.
+function myFunction()
+
+{
+}
+
+// Space before brace.
+function myFunction()
+ {
+}
+
+class myClass()
+{
+ // Brace should be on new line.
+ function myFunction() {
+ }
+
+ // Good.
+ function myFunction()
+ {
+ }
+
+ // No aligned correctly.
+ function myFunction()
+{
+}
+
+ // Too many spaces.
+ function myFunction() {
+ }
+
+ // Too many newlines.
+ function myFunction()
+
+ {
+ }
+
+ // Space before brace.
+ function myFunction()
+ {
+ }
+}
+
+
+
+/* Multi-line declarations */
+
+
+
+// Brace should be on new line.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+}
+
+// Good.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+{
+}
+
+// Too many spaces.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+}
+
+// Too many newlines.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+
+{
+}
+
+// Space before brace.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+ {
+}
+
+class myClass()
+{
+ // Brace should be on new line.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+ }
+
+ // Good.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+ {
+ }
+
+ // No aligned correctly.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+{
+}
+
+ // Too many spaces.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+ }
+
+ // Too many newlines.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+
+ {
+ }
+
+ // Space before brace.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+ {
+ }
+}
+
+interface MyInterface()
+{
+ function myFunction();
+}
+
+function myFunction(
+ $arg1,
+ $arg2,
+ $arg3,
+ $arg4,
+ $arg5,
+ $arg6
+ )
+{
+}
+
+function myFunction(
+ $arg1,
+ $arg2,
+ $arg3,
+ $arg4,
+ $arg5,
+ $arg6
+ ) {
+}
+
+function myFunction() {}
+function myFunction()
+{}
+
+// @codingStandardsChangeSetting Generic.Functions.OpeningFunctionBraceBsdAllman checkClosures 1
+
+$closureWithArgs = function ($arg1, $arg2) {
+ // body
+};
+
+$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
+ // body
+};
+
+$test = function ($param) use ($result) {
+ return null;
+}
+
+$test = function ($param) use ($result) : Something {
+ return null;
+};
+
+// @codingStandardsChangeSetting Generic.Functions.OpeningFunctionBraceBsdAllman checkClosures 0
+
+$closureWithArgs = function ($arg1, $arg2) {
+ // body
+};
+
+class Foo
+{
+//Comments should not affect code
+ public function bar()
+ {
+
+ }
+//Comments should not affect code
+}
+
+function myFunction() : Something {
+ return null;
+}
--- /dev/null
+<?php
+
+// Brace should be on new line.
+function myFunction()
+{
+}
+
+// Good.
+function myFunction()
+{
+}
+
+// Too many spaces.
+function myFunction()
+{
+}
+
+// Too many newlines.
+function myFunction()
+{
+}
+
+// Space before brace.
+function myFunction()
+{
+}
+
+class myClass()
+{
+ // Brace should be on new line.
+ function myFunction()
+ {
+ }
+
+ // Good.
+ function myFunction()
+ {
+ }
+
+ // No aligned correctly.
+ function myFunction()
+ {
+}
+
+ // Too many spaces.
+ function myFunction()
+ {
+ }
+
+ // Too many newlines.
+ function myFunction()
+ {
+ }
+
+ // Space before brace.
+ function myFunction()
+ {
+ }
+}
+
+
+
+/* Multi-line declarations */
+
+
+
+// Brace should be on new line.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+{
+}
+
+// Good.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+{
+}
+
+// Too many spaces.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+{
+}
+
+// Too many newlines.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+{
+}
+
+// Space before brace.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+{
+}
+
+class myClass()
+{
+ // Brace should be on new line.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+ {
+ }
+
+ // Good.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+ {
+ }
+
+ // No aligned correctly.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+ {
+}
+
+ // Too many spaces.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+ {
+ }
+
+ // Too many newlines.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+ {
+ }
+
+ // Space before brace.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+ {
+ }
+}
+
+interface MyInterface()
+{
+ function myFunction();
+}
+
+function myFunction(
+ $arg1,
+ $arg2,
+ $arg3,
+ $arg4,
+ $arg5,
+ $arg6
+ )
+{
+}
+
+function myFunction(
+ $arg1,
+ $arg2,
+ $arg3,
+ $arg4,
+ $arg5,
+ $arg6
+ )
+{
+}
+
+function myFunction()
+{}
+function myFunction()
+{}
+
+// @codingStandardsChangeSetting Generic.Functions.OpeningFunctionBraceBsdAllman checkClosures 1
+
+$closureWithArgs = function ($arg1, $arg2)
+{
+ // body
+};
+
+$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2)
+{
+ // body
+};
+
+$test = function ($param) use ($result)
+{
+ return null;
+}
+
+$test = function ($param) use ($result) : Something
+{
+ return null;
+};
+
+// @codingStandardsChangeSetting Generic.Functions.OpeningFunctionBraceBsdAllman checkClosures 0
+
+$closureWithArgs = function ($arg1, $arg2) {
+ // body
+};
+
+class Foo
+{
+//Comments should not affect code
+ public function bar()
+ {
+
+ }
+//Comments should not affect code
+}
+
+function myFunction() : Something
+{
+ return null;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the OpeningFunctionBraceBsdAllman 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\Standards\Generic\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class OpeningFunctionBraceBsdAllmanUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 4 => 1,
+ 13 => 1,
+ 19 => 1,
+ 24 => 1,
+ 30 => 1,
+ 40 => 1,
+ 44 => 1,
+ 50 => 1,
+ 55 => 1,
+ 67 => 1,
+ 78 => 1,
+ 85 => 1,
+ 91 => 1,
+ 98 => 1,
+ 110 => 1,
+ 115 => 1,
+ 122 => 1,
+ 128 => 1,
+ 155 => 1,
+ 158 => 1,
+ 164 => 1,
+ 168 => 1,
+ 172 => 1,
+ 176 => 1,
+ 196 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// Good.
+function myFunction() {
+}
+
+// Brace should be on same line.
+function myFunction()
+{
+}
+
+// Too many spaces.
+function myFunction() {
+}
+
+// Uses tab.
+function myFunction() {
+}
+
+
+class myClass()
+{
+ // Good.
+ function myFunction() {
+ }
+
+ // Brace should be on same line.
+ function myFunction()
+ {
+ }
+
+ // Too many spaces.
+ function myFunction() {
+ }
+
+ // Uses tab.
+ function myFunction() {
+ }
+}
+
+
+
+/* Multi-line declarations */
+
+// Good.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+}
+
+// Brace should be on same line.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+{
+}
+
+// Too many spaces.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+}
+
+// Uses tab.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+}
+
+
+class myClass()
+{
+ // Good.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+ }
+
+ // Brace should be on same line.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4)
+ {
+ }
+
+ // Too many spaces.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+ }
+
+ // Uses tab.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+ }
+}
+
+interface MyInterface()
+{
+ function myFunction();
+}
+
+function myFunction(
+ $arg1,
+ $arg2,
+ $arg3,
+ $arg4,
+ $arg5,
+ $arg6
+ )
+{
+}
+
+function myFunction(
+ $arg1,
+ $arg2,
+ $arg3,
+ $arg4,
+ $arg5,
+ $arg6
+ ) {
+}
+
+function myFunction() {}
+function myFunction()
+{}
+
+// @codingStandardsChangeSetting Generic.Functions.OpeningFunctionBraceKernighanRitchie checkClosures 1
+
+$closureWithArgs = function ($arg1, $arg2){
+ // body
+};
+
+$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2){
+ // body
+};
+
+$test = function ($param) use ($result)
+{
+ return null;
+}
+
+$test = function ($param) use ($result) : Something
+{
+ return null;
+}
+
+$test = function ($param) use ($result): Something
+{
+ return null;
+}
+
+// @codingStandardsChangeSetting Generic.Functions.OpeningFunctionBraceKernighanRitchie checkClosures 0
+
+$closureWithArgs = function ($arg1, $arg2){
+ // body
+};
+
+function myFunction() : Something
+{
+ return null;
+}
+
+function myFunction() : Something // Break me
+{
+ return null;
+}
+
+function myFunction(): Something {
+ return null;
+}
+
+function myFunction(): Something
+{
+ return null;
+}
--- /dev/null
+<?php
+
+// Good.
+function myFunction() {
+}
+
+// Brace should be on same line.
+function myFunction() {
+}
+
+// Too many spaces.
+function myFunction() {
+}
+
+// Uses tab.
+function myFunction() {
+}
+
+
+class myClass()
+{
+ // Good.
+ function myFunction() {
+ }
+
+ // Brace should be on same line.
+ function myFunction() {
+ }
+
+ // Too many spaces.
+ function myFunction() {
+ }
+
+ // Uses tab.
+ function myFunction() {
+ }
+}
+
+
+
+/* Multi-line declarations */
+
+// Good.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+}
+
+// Brace should be on same line.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+}
+
+// Too many spaces.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+}
+
+// Uses tab.
+function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+}
+
+
+class myClass()
+{
+ // Good.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+ }
+
+ // Brace should be on same line.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+ }
+
+ // Too many spaces.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+ }
+
+ // Uses tab.
+ function myFunction($variable1, $variable2,
+ $variable3, $variable4) {
+ }
+}
+
+interface MyInterface()
+{
+ function myFunction();
+}
+
+function myFunction(
+ $arg1,
+ $arg2,
+ $arg3,
+ $arg4,
+ $arg5,
+ $arg6
+ ) {
+}
+
+function myFunction(
+ $arg1,
+ $arg2,
+ $arg3,
+ $arg4,
+ $arg5,
+ $arg6
+ ) {
+}
+
+function myFunction() {}
+function myFunction() {
+}
+
+// @codingStandardsChangeSetting Generic.Functions.OpeningFunctionBraceKernighanRitchie checkClosures 1
+
+$closureWithArgs = function ($arg1, $arg2) {
+ // body
+};
+
+$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
+ // body
+};
+
+$test = function ($param) use ($result) {
+ return null;
+}
+
+$test = function ($param) use ($result) : Something {
+ return null;
+}
+
+$test = function ($param) use ($result): Something {
+ return null;
+}
+
+// @codingStandardsChangeSetting Generic.Functions.OpeningFunctionBraceKernighanRitchie checkClosures 0
+
+$closureWithArgs = function ($arg1, $arg2){
+ // body
+};
+
+function myFunction() : Something {
+ return null;
+}
+
+function myFunction() : Something {
+ // Break me
+ return null;
+}
+
+function myFunction(): Something {
+ return null;
+}
+
+function myFunction(): Something {
+ return null;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the OpeningFunctionBraceKernighanRitchie 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\Standards\Generic\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class OpeningFunctionBraceKernighanRitchieUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 9 => 1,
+ 13 => 1,
+ 17 => 1,
+ 29 => 1,
+ 33 => 1,
+ 37 => 1,
+ 53 => 1,
+ 58 => 1,
+ 63 => 1,
+ 77 => 1,
+ 82 => 1,
+ 87 => 1,
+ 104 => 1,
+ 119 => 1,
+ 123 => 1,
+ 127 => 1,
+ 132 => 1,
+ 137 => 1,
+ 142 => 1,
+ 153 => 1,
+ 158 => 1,
+ 167 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+function complexityOne() { }
+
+function comlexityFive()
+{
+ if ($condition) {
+ }
+
+ switch ($condition) {
+ case '1':
+ break;
+ case '2':
+ break;
+ case '3':
+ break;
+ }
+}
+
+function comlexityTen()
+{
+ while ($condition === true) {
+ if ($condition) {
+ }
+ }
+
+ switch ($condition) {
+ case '1':
+ if ($condition) {
+ } else if ($cond) {
+ }
+ break;
+ case '2':
+ while ($cond) {
+ echo 'hi';
+ }
+ break;
+ case '3':
+ break;
+ default:
+ break;
+ }
+}
+
+function comlexityEleven()
+{
+ while ($condition === true) {
+ if ($condition) {
+ } else if ($cond) {
+ }
+ }
+
+ switch ($condition) {
+ case '1':
+ if ($condition) {
+ } else if ($cond) {
+ }
+ break;
+ case '2':
+ while ($cond) {
+ echo 'hi';
+ }
+ break;
+ case '3':
+ break;
+ default:
+ break;
+ }
+}
+
+
+function comlexityTwenty()
+{
+ while ($condition === true) {
+ if ($condition) {
+ } else if ($cond) {
+ }
+ }
+
+ switch ($condition) {
+ case '1':
+ if ($condition) {
+ } else if ($cond) {
+ }
+ break;
+ case '2':
+ while ($cond) {
+ echo 'hi';
+ }
+ break;
+ case '3':
+ switch ($cond) {
+ case '1':
+ break;
+ case '2':
+ break;
+ }
+ break;
+ case '4':
+ do {
+ if ($condition) {
+ if ($cond) {
+ } else if ($con) {
+ }
+ }
+ } while ($cond);
+ break;
+ default:
+ if ($condition) {
+ }
+ break;
+ }
+}
+
+
+function comlexityTwentyOne()
+{
+ while ($condition === true) {
+ if ($condition) {
+ } else if ($cond) {
+ }
+ }
+
+ switch ($condition) {
+ case '1':
+ if ($condition) {
+ } else if ($cond) {
+ }
+ break;
+ case '2':
+ while ($cond) {
+ echo 'hi';
+ }
+ break;
+ case '3':
+ switch ($cond) {
+ case '1':
+ break;
+ case '2':
+ break;
+ }
+ break;
+ case '4':
+ do {
+ if ($condition) {
+ if ($cond) {
+ } else if ($con) {
+ }
+ }
+ } while ($cond);
+ break;
+ default:
+ if ($condition) {
+ } else if ($cond) {
+ }
+ break;
+ }
+}
+
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the CyclomaticComplexity 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\Standards\Generic\Tests\Metrics;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class CyclomaticComplexityUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(116 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 45 => 1,
+ 72 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+function nestingOne()
+{
+ if ($condition) {
+ echo 'hi';
+ }
+}
+
+function nestingFive()
+{
+ if ($condition) {
+ echo 'hi';
+ switch ($condition)
+ {
+ case '1':
+ if ($condition === '1') {
+ if ($cond) {
+ echo 'hi';
+ }
+ }
+ break;
+ }
+ }
+}
+
+function nestingSix()
+{
+ if ($condition) {
+ echo 'hi';
+ switch ($condition)
+ {
+ case '1':
+ if ($condition === '1') {
+ if ($cond) {
+ foreach ($conds as $cond) {
+ echo 'hi';
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+function nestingTen()
+{
+ if ($condition) {
+ echo 'hi';
+ switch ($condition)
+ {
+ case '1':
+ if ($condition === '1') {
+ if ($cond) {
+ switch ($cond) {
+ case '1':
+ if ($cond === '1') {
+ foreach ($conds as $cond) {
+ if ($cond === 'hi') {
+ echo 'hi';
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+function nestingEleven()
+{
+ if ($condition) {
+ echo 'hi';
+ switch ($condition)
+ {
+ case '1':
+ if ($condition === '1') {
+ if ($cond) {
+ switch ($cond) {
+ case '1':
+ if ($cond === '1') {
+ foreach ($conds as $cond) {
+ if ($cond === 'hi') {
+ if ($cond !== 'bye') {
+ echo 'hi';
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the NestingLevel 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\Standards\Generic\Tests\Metrics;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class NestingLevelUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(73 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 27 => 1,
+ 46 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+abstract class My_Class {
+
+ public function __construct() {}
+ public function My_Class() {}
+ public function _My_Class() {}
+
+ public function getSomeValue() {}
+ public function parseMyDSN() {}
+ public function get_some_value() {}
+ public function GetSomeValue() {}
+ public function getSomeValue_Again() {}
+
+ protected function getSomeValue() {}
+ protected function parseMyDSN() {}
+ protected function get_some_value() {}
+
+ private function _getSomeValue() {}
+ private function parseMyDSN() {}
+ private function _get_some_value() {}
+
+ function getSomeValue() {}
+ function parseMyDSN() {}
+ function get_some_value() {}
+
+}//end class
+
+function getSomeValue() {}
+function parseMyDSN() {}
+function get_some_value() {}
+
+
+/* Test for magic functions */
+
+class Magic_Test {
+ function __construct() {}
+ function __destruct() {}
+ function __call() {}
+ function __callStatic() {}
+ function __get() {}
+ function __set() {}
+ function __isset() {}
+ function __unset() {}
+ function __sleep() {}
+ function __wakeup() {}
+ function __toString() {}
+ function __set_state() {}
+ function __clone() {}
+ function __autoload() {}
+ function __invoke() {}
+ function __myFunction() {}
+ function __my_function() {}
+ function __call() {}
+}
+
+function __construct() {}
+function __destruct() {}
+function __call() {}
+function __callStatic() {}
+function __get() {}
+function __set() {}
+function __isset() {}
+function __unset() {}
+function __sleep() {}
+function __wakeup() {}
+function __toString() {}
+function __set_state() {}
+function __clone() {}
+function __autoload() {}
+function __invoke() {}
+function __myFunction() {}
+function __my_function() {}
+function __call() {}
+
+class Closure_Test {
+ function test() {
+ $foo = function() { echo 'foo'; };
+ }
+}
+
+function test() {
+ $foo = function() { echo 'foo'; };
+}
+
+/* @codingStandardsIgnoreStart */
+class MyClass
+{
+ /* @codingStandardsIgnoreEnd */
+ public function __construct() {}
+}
+
+trait Foo
+{
+ function __call() {}
+}
+
+class Magic_Case_Test {
+ function __Construct() {}
+ function __isSet() {}
+ function __tostring() {}
+}
+function __autoLoad() {}
+
+class Foo extends \SoapClient
+{
+ public function __soapCall(
+ $functionName,
+ $arguments,
+ $options = array(),
+ $inputHeaders = null,
+ &$outputHeaders = array()
+ ) {
+ // body
+ }
+}
+
+function __debugInfo() {}
+class Foo {
+ function __debugInfo() {}
+}
+
+function ___tripleUnderscore() {} // Ok.
+
+class triple {
+ public function ___tripleUnderscore() {} // Ok.
+}
+
+/* Magic methods in anonymous classes. */
+$a = new class {
+ function __construct() {}
+ function __destruct() {}
+ function __call() {}
+ function __callStatic() {}
+ function __get() {}
+ function __set() {}
+ function __isset() {}
+ function __unset() {}
+ function __sleep() {}
+ function __wakeup() {}
+ function __toString() {}
+ function __set_state() {}
+ function __clone() {}
+ function __autoload() {}
+ function __invoke() {}
+ function __myFunction() {}
+ function __my_function() {}
+ function __call() {}
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the CamelCapsFunctionName 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\Standards\Generic\Tests\NamingConventions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class CamelCapsFunctionNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ $errors = array(
+ 10 => 1,
+ 11 => 1,
+ 12 => 1,
+ 13 => 1,
+ 16 => 1,
+ 17 => 1,
+ 20 => 1,
+ 21 => 1,
+ 24 => 1,
+ 25 => 1,
+ 30 => 1,
+ 31 => 1,
+ 50 => 1,
+ 52 => 1,
+ 53 => 1,
+ 57 => 1,
+ 58 => 1,
+ 59 => 1,
+ 60 => 1,
+ 61 => 1,
+ 62 => 1,
+ 63 => 1,
+ 64 => 1,
+ 65 => 1,
+ 66 => 1,
+ 67 => 1,
+ 68 => 1,
+ 69 => 1,
+ 71 => 1,
+ 72 => 1,
+ 73 => 1,
+ 74 => 1,
+ 118 => 1,
+ 144 => 1,
+ 146 => 1,
+ 147 => 1,
+ );
+
+ return $errors;
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class TestClass extends MyClass
+{
+
+ function TestClass() {
+ parent::MyClass();
+ parent::__construct();
+ }
+
+ function __construct() {
+ parent::MyClass();
+ parent::__construct();
+ }
+
+}
+
+class MyClass
+{
+
+ function MyClass() {
+ parent::YourClass();
+ parent::__construct();
+ }
+
+ function __construct() {
+ parent::YourClass();
+ parent::__construct();
+ }
+
+}
+
+class MyClass extends \MyNamespace\SomeClass
+{
+ function __construct() {
+ something::MyNamespace();
+ }
+
+}
+
+abstract class MyAbstractClass extends \MyNamespace\SomeClass
+{
+ abstract public function __construct();
+}
+
+class OldClass
+{
+ function OldClass()
+ {
+
+ }
+}
+
+foo(new class extends MyClass
+{
+
+ function TestClass() {
+ parent::MyClass();
+ parent::__construct();
+ }
+
+ function __construct() {
+ parent::MyClass();
+ parent::__construct();
+ }
+
+});
--- /dev/null
+<?php
+/**
+ * Unit test class for the ConstructorName 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\Standards\Generic\Tests\NamingConventions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ConstructorNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 6 => 1,
+ 11 => 1,
+ 47 => 1,
+ 62 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+use Exception as My_Exception, foo\bar, baz;
+namespace foo;
+namespace foo\bar;
+namespace bar\foo\baz;
+
+define('VALID_NAME', true);
+define('invalidName', true);
+define("VALID_NAME", true);
+define("invalidName", true);
+define('bar\foo\baz\VALID_NAME_WITH_NAMESPACE', true);
+define('bar\foo\baz\invalidNameWithNamespace', true);
+define("bar\foo\baz\VALID_NAME_WITH_NAMESPACE", true);
+define("bar\foo\baz\invalidNameWithNamespace", true);
+
+class TestClass extends MyClass, YourClass
+{
+
+ const const1 = 'hello';
+ const CONST2 = 'hello';
+
+ function test()
+ {
+ echo constant('VALID_NAME');
+ echo VALID_NAME;
+ print VALID_NAME;
+ echo(VALID_NAME);
+ print(VALID_NAME);
+ echo constant('invalidName');
+ echo invalidName;
+ print invalidName;
+ echo(invalidName);
+ print(invalidName);
+ echo constant('VALID_NAME_WITH_NAMESPACE');
+ echo VALID_NAME_WITH_NAMESPACE;
+ print VALID_NAME_WITH_NAMESPACE;
+ echo(VALID_NAME_WITH_NAMESPACE);
+ print(VALID_NAME_WITH_NAMESPACE);
+ echo constant('invalidNameWithNamespace');
+ echo invalidNameWithNamespace;
+ print invalidNameWithNamespace;
+ echo(invalidNameWithNamespace);
+ print(invalidNameWithNamespace);
+
+ echo constant("VALID_NAME");
+ echo constant("invalidName");
+ echo constant("VALID_NAME_WITH_NAMESPACE");
+ echo constant("invalidNameWithNamespace");
+
+ echo 'Hello', VALID_NAME;
+ echo 'Hello', invalidName;
+ echo 'Hello', VALID_NAME_WITH_NAMESPACE;
+ echo 'Hello', invalidNameWithNamespace;
+
+ // These might look like constants to
+ // poorly written code.
+ echo 'Hello there';
+ echo "HELLO";
+ echo 'HELLO';
+ print 'Hello there';
+ print "HELLO";
+ print 'HELLO';
+ }
+
+ function myFunc(PHP_CodeSniffer &$blah) {}
+ function myFunc(PHP_CodeSniffer $blah) {}
+
+}
+
+interface MyInterface
+{
+}
+
+if (($object instanceof Some_Class) === false) {
+ $var = <<<EOF
+This is some heredoc text.
+This is some heredoc text.
+This is some heredoc text.
+
+This is some heredoc text.
+This is some heredoc text.
+This is some heredoc text.
+EOF;
+}
+
+$var = <<<EOF
+This is some heredoc text.
+This is some heredoc text.
+This is some heredoc text.
+
+This is some heredoc text.
+This is some heredoc text.
+This is some heredoc text.
+EOF;
+
+throw new InvalidSomethingException;
+
+declare(ticks = 1) {
+ foreach ($var as $bit) {
+ echo $bit;
+ }
+}
+
+$binary = (binary) $string;
+
+$foo->define('bar');
+$foo->getBar()->define('foo');
+
+// This is allowed as it is required for PHPUnit.
+if (PHPUnit_MAIN_METHOD == 'PHP_Shell_AllTests::main') {
+ PHP_Shell_AllTests::main();
+}
+
+class ConstTest {
+ const TESTER = '1';
+ public function __construct() {
+ echo constant('self::TESTER');
+ echo constant('self::tester');
+ }
+}
+
+class A {
+ public static function constant($x) {}
+}
+
+A::constant('anything-not-in-uppercase');
+
+// goto
+goto gotolabel;
+
+gototarget1:
+
+function ConstTest() {
+ gototarget2:
+}
+
+switch($foo)
+{
+ case $bar:
+ gototarget3:
+}
+
+$a = 2 * ${x} - ${minus};
+
+class Object implements IObject
+{
+ use TProperties;
+ use TReflected {
+ TReflected::reflect insteadof TProperties;
+ }
+ use ListenerAggregateTrait, PreLoadUserTrait, PreLoadCompanyTrait {
+ PreloadUserTrait::PreLoadUser as loadUser;
+ PreloadCompanyTrait::PreLoadCompany as loadCompany;
+ }
+}
+
+class MyClass {
+ use Something {
+ delete as protected blah;
+ }
+}
+
+class Foo
+{
+ public function bar()
+ {
+ return Bar::class;
+ }
+}
+
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the ValidConstantName 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\Standards\Generic\Tests\NamingConventions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class UpperCaseConstantNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 8 => 1,
+ 10 => 1,
+ 12 => 1,
+ 14 => 1,
+ 19 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$output = `ls -al`;
--- /dev/null
+<?php
+/**
+ * Unit test class for the BacktickOperator 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\Standards\Generic\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class BacktickOperatorUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(2 => 2);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+Foo
+<?php
+// Some code
+?>
+
+
+ <?php
+// Some code
+?>
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env php
+<?php
+
+//some code
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the CharacterBeforePHPOpeningTag sniff.
+ *
+ * @author Andy Grunwald <andygrunwald@gmail.com>
+ * @copyright 2010-2014 Andy Grunwald
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class CharacterBeforePHPOpeningTagUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ switch ($testFile) {
+ case 'CharacterBeforePHPOpeningTagUnitTest.1.inc':
+ return array(2 => 1);
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+echo 'foo';
+?>
+<b>Bold text</b>
+<?php
+echo 'bar';
+?>
+<i>Italic text</i>
+<?php
+echo 'baz';
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClosingPHPTag sniff.
+ *
+ * @author Andy Grunwald <andygrunwald@gmail.com>
+ * @copyright 2010-2014 Andy Grunwald
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClosingPHPTagUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(9 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<div>
+<?php echo $var; ?>
+Some content here.
+<script language="php">
+echo $var;
+</script>
+<script language='php'>echo $var;</script>
+<script type="text/php" language="php">
+echo $var;
+</script>
+<script language='PHP' type='text/php'>
+echo $var;
+</script>
+</div>
--- /dev/null
+<div>
+<?php echo $var; ?>
+Some content here.
+<?php
+echo $var;
+?>
+<?php echo $var;?>
+<script type="text/php" language="php">
+echo $var;
+</script>
+<script language='PHP' type='text/php'>
+echo $var;
+</script>
+</div>
--- /dev/null
+<div>
+<% echo $var; %>
+<p>Some text <% echo $var; %> and some more text</p>
+<%= $var . ' and some more text to make sure the snippet works'; %>
+<p>Some text <%= $var %> and some more text</p>
+</div>
--- /dev/null
+<div>
+<?php echo $var; ?>
+<p>Some text <?php echo $var; ?> and some more text</p>
+<?php echo $var . ' and some more text to make sure the snippet works'; ?>
+<p>Some text <?php echo $var ?> and some more text</p>
+</div>
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowAlternativePHPTags 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\Standards\Generic\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowAlternativePHPTagsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Get a list of all test files to check.
+ *
+ * @param string $testFileBase The base path that the unit tests files will have.
+ *
+ * @return string[]
+ */
+ protected function getTestFiles($testFileBase)
+ {
+ $testFiles = array($testFileBase.'1.inc');
+
+ $aspTags = false;
+ if (PHP_VERSION_ID < 70000) {
+ $aspTags = (boolean) ini_get('asp_tags');
+ }
+
+ if ($aspTags === true) {
+ $testFiles[] = $testFileBase.'2.inc';
+ }
+
+ return $testFiles;
+
+ }//end getTestFiles()
+
+
+ /**
+ * Skip this test on HHVM.
+ *
+ * @return bool Whether to skip this test.
+ */
+ protected function shouldSkipTest()
+ {
+ return defined('HHVM_VERSION');
+
+ }//end shouldSkipTest()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ switch ($testFile) {
+ case 'DisallowAlternativePHPTagsUnitTest.1.inc':
+ return array(
+ 4 => 1,
+ 7 => 1,
+ 8 => 1,
+ 11 => 1,
+ );
+ case 'DisallowAlternativePHPTagsUnitTest.2.inc':
+ return array(
+ 2 => 1,
+ 3 => 1,
+ 4 => 1,
+ 5 => 1,
+ );
+ default:
+ return array();
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<div>
+<?php echo $var; ?>
+Some content here.
+
+<?= $var; ?>
+Some content <?= $var ?> Some more content
+<?=
+$var;
+?>
+<?=$var; ?>
+</div>
--- /dev/null
+<div>
+<?php echo $var; ?>
+Some content here.
+
+<?php echo $var; ?>
+Some content <?php echo $var ?> Some more content
+<?php echo
+$var;
+?>
+<?php echo $var; ?>
+</div>
--- /dev/null
+<div>
+<? echo $var; ?>
+Some content <? echo $var ?> Some more content
+<?
+echo $var;
+?>
+<?echo $var; ?>
+</div>
--- /dev/null
+<div>
+<?php echo $var; ?>
+Some content <?php echo $var ?> Some more content
+<?php
+echo $var;
+?>
+<?php echo $var; ?>
+</div>
--- /dev/null
+// Test warning for when short_open_tag is off.
+
+Some content <? echo $var; ?> Some more content
+
+// Test multi-line.
+Some content <?
+echo $var;
+?> Some more content
+
+// Make sure skipping works.
+Some content <?
+echo '<?';
+?> Some more content
+
+// Only recognize closing tag after opener.
+Some?> content <?
--- /dev/null
+<div>
+<?php echo $var; ?>
+Some content here.
+<?= $var; ?>
+<? echo $var; ?>
+</div>
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowShortOpenTag 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\Standards\Generic\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowShortOpenTagUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Get a list of all test files to check.
+ *
+ * @param string $testFileBase The base path that the unit tests files will have.
+ *
+ * @return string[]
+ */
+ protected function getTestFiles($testFileBase)
+ {
+ $testFiles = array($testFileBase.'1.inc');
+
+ $option = (boolean) ini_get('short_open_tag');
+ if ($option === true || defined('HHVM_VERSION') === true) {
+ $testFiles[] = $testFileBase.'2.inc';
+ } else {
+ $testFiles[] = $testFileBase.'3.inc';
+ }
+
+ return $testFiles;
+
+ }//end getTestFiles()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ switch ($testFile) {
+ case 'DisallowShortOpenTagUnitTest.1.inc':
+ if (PHP_VERSION_ID < 50400) {
+ $option = (boolean) ini_get('short_open_tag');
+ if ($option === false) {
+ // Short open tags are off and PHP isn't doing short echo by default.
+ return array();
+ }
+ }
+ return array(
+ 5 => 1,
+ 6 => 1,
+ 7 => 1,
+ 10 => 1,
+ );
+ case 'DisallowShortOpenTagUnitTest.2.inc':
+ return array(
+ 2 => 1,
+ 3 => 1,
+ 4 => 1,
+ 7 => 1,
+ );
+ default:
+ return array();
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='')
+ {
+ switch ($testFile) {
+ case 'DisallowShortOpenTagUnitTest.1.inc':
+ if (PHP_VERSION_ID < 50400) {
+ $option = (boolean) ini_get('short_open_tag');
+ if ($option === false) {
+ // Short open tags are off and PHP isn't doing short echo by default.
+ return array(
+ 5 => 1,
+ 6 => 1,
+ 7 => 1,
+ 10 => 1,
+ );
+ }
+ }
+ return array();
+ case 'DisallowShortOpenTagUnitTest.3.inc':
+ return array(
+ 3 => 1,
+ 6 => 1,
+ 11 => 1,
+ );
+ default:
+ return array();
+ }//end switch
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$size = sizeof($array);
+$size = count($array);
+delete($filepath);
+unset($filepath);
+$size = \sizeof($array);
+$size = \my\ns\sizeof('abc');($array);
+
+// No errors thrown for class methods.
+$size = MyClass::sizeof($array);
+$size = MyClass::count($array);
+MyClass::delete($filepath);
+MyClass::unset($filepath);
+
+$size = $class->sizeof($array);
+$size = $class->count($array);
+$class->delete($filepath);
+$class->unset($filepath);
+
+function delete() {}
+function unset() {}
+function sizeof() {}
+function count() {}
+
+trait DelProvider {
+ public function delete() {
+ //irrelevant
+ }
+}
+
+class LeftSideTest {
+ use DelProvider {
+ delete as protected unsetter;
+ }
+}
+
+class RightSideTest {
+ use DelProvider {
+ delete as delete;
+ }
+}
+
+class RightSideVisTest {
+ use DelProvider {
+ delete as protected delete;
+ delete insteadof delete;
+ }
+}
+
+namespace Something\sizeof;
+$var = new Sizeof();
+class SizeOf implements Something {}
+
+function mymodule_form_callback(SizeOf $sizeof) {
+}
+
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the ForbiddenFunctions 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\Standards\Generic\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ForbiddenFunctionsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ $errors = array(
+ 2 => 1,
+ 4 => 1,
+ 6 => 1,
+ );
+
+ return $errors;
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// True
+function myFunction($arg1, $arg2=true)
+{
+}
+function myFunction($arg1, $arg2=TRUE)
+{
+}
+function myFunction($arg1, $arg2=True)
+{
+}
+
+if ($variable === true) { }
+if ($variable === TRUE) { }
+if ($variable === True) { }
+
+
+// False
+function myFunction($arg1, $arg2=false)
+{
+}
+function myFunction($arg1, $arg2=FALSE)
+{
+}
+function myFunction($arg1, $arg2=False)
+{
+}
+
+if ($variable === false) { }
+if ($variable === FALSE) { }
+if ($variable === False) { }
+
+
+// Null
+function myFunction($arg1, $arg2=null)
+{
+}
+function myFunction($arg1, $arg2=NULL)
+{
+}
+function myFunction($arg1, $arg2=Null)
+{
+}
+
+if ($variable === null) { }
+if ($variable === NULL) { }
+if ($variable === Null) { }
+
+$x = new stdClass();
+$x->NULL = 7;
+
+use Zend\Log\Writer\NULL as NullWriter;
+new \Zend\Log\Writer\NULL();
+
+namespace False;
+
+class True extends Null implements False {}
+
+use True\Something;
+use Something\True;
+class MyClass
+{
+ public function myFunction()
+ {
+ $var = array('foo' => new True());
+ }
+}
+
+$x = $f?FALSE:true;
+$x = $f? FALSE:true;
+
+class MyClass
+{
+ // Spice things up a little.
+ const TRUE = false;
+}
+
+var_dump(MyClass::TRUE);
+
+function tRUE() {}
+
+$input->getFilterChain()->attachByName('Null', ['type' => Null::TYPE_STRING]);
--- /dev/null
+<?php
+
+// True
+function myFunction($arg1, $arg2=true)
+{
+}
+function myFunction($arg1, $arg2=true)
+{
+}
+function myFunction($arg1, $arg2=true)
+{
+}
+
+if ($variable === true) { }
+if ($variable === true) { }
+if ($variable === true) { }
+
+
+// False
+function myFunction($arg1, $arg2=false)
+{
+}
+function myFunction($arg1, $arg2=false)
+{
+}
+function myFunction($arg1, $arg2=false)
+{
+}
+
+if ($variable === false) { }
+if ($variable === false) { }
+if ($variable === false) { }
+
+
+// Null
+function myFunction($arg1, $arg2=null)
+{
+}
+function myFunction($arg1, $arg2=null)
+{
+}
+function myFunction($arg1, $arg2=null)
+{
+}
+
+if ($variable === null) { }
+if ($variable === null) { }
+if ($variable === null) { }
+
+$x = new stdClass();
+$x->NULL = 7;
+
+use Zend\Log\Writer\NULL as NullWriter;
+new \Zend\Log\Writer\NULL();
+
+namespace False;
+
+class True extends Null implements False {}
+
+use True\Something;
+use Something\True;
+class MyClass
+{
+ public function myFunction()
+ {
+ $var = array('foo' => new True());
+ }
+}
+
+$x = $f?false:true;
+$x = $f? false:true;
+
+class MyClass
+{
+ // Spice things up a little.
+ const TRUE = false;
+}
+
+var_dump(MyClass::TRUE);
+
+function tRUE() {}
+
+$input->getFilterChain()->attachByName('Null', ['type' => Null::TYPE_STRING]);
--- /dev/null
+if (variable === true) { }
+if (variable === TRUE) { }
+if (variable === True) { }
+variable = True;
+
+if (variable === false) { }
+if (variable === FALSE) { }
+if (variable === False) { }
+variable = false;
+
+if (variable === null) { }
+if (variable === NULL) { }
+if (variable === Null) { }
+variable = NULL;
--- /dev/null
+if (variable === true) { }
+if (variable === true) { }
+if (variable === true) { }
+variable = true;
+
+if (variable === false) { }
+if (variable === false) { }
+if (variable === false) { }
+variable = false;
+
+if (variable === null) { }
+if (variable === null) { }
+if (variable === null) { }
+variable = null;
--- /dev/null
+<?php
+/**
+ * Unit test class for the LowerCaseConstant 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\Standards\Generic\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LowerCaseConstantUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='LowerCaseConstantUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'LowerCaseConstantUnitTest.inc':
+ return array(
+ 7 => 1,
+ 10 => 1,
+ 15 => 1,
+ 16 => 1,
+ 23 => 1,
+ 26 => 1,
+ 31 => 1,
+ 32 => 1,
+ 39 => 1,
+ 42 => 1,
+ 47 => 1,
+ 48 => 1,
+ 70 => 1,
+ 71 => 1,
+ );
+ break;
+ case 'LowerCaseConstantUnitTest.js':
+ return array(
+ 2 => 1,
+ 3 => 1,
+ 4 => 1,
+ 7 => 1,
+ 8 => 1,
+ 12 => 1,
+ 13 => 1,
+ 14 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+// A few keywords. Obviously not valid syntax.
+class Something extends SomethingElse implements Whatever
+abstract public private protected function whatever() {}
+const array()
+for ($var as $var) { exit; }
+if ($a and $b or $c xor $d) { die; } elseif { } else
+goto a;
+
+Class Something EXTENDS SomethingElse implementS Whatever
+Abstract Public Private Protected function whatever() {}
+CONST array()
+For ($var As $var) { Exit; }
+If ($a AND $b OR $c XOR $d) { Die; } ElseIf { } Else
+GOTO a;
+function (Array $a) {}
+const PRIVATE;
+HttpStatus::CONTINUE;
+Function ($f) {
+ Yield $f;
+ Yield From fun();
+}
+class X extends Y {
+ public function m() {
+ Parent::m();
+ }
+ public function n() {
+ Self::n();
+ }
+}
+function
--- /dev/null
+<?php
+// A few keywords. Obviously not valid syntax.
+class Something extends SomethingElse implements Whatever
+abstract public private protected function whatever() {}
+const array()
+for ($var as $var) { exit; }
+if ($a and $b or $c xor $d) { die; } elseif { } else
+goto a;
+
+class Something extends SomethingElse implements Whatever
+abstract public private protected function whatever() {}
+const array()
+for ($var as $var) { exit; }
+if ($a and $b or $c xor $d) { die; } elseif { } else
+goto a;
+function (array $a) {}
+const PRIVATE;
+HttpStatus::CONTINUE;
+function ($f) {
+ yield $f;
+ yield from fun();
+}
+class X extends Y {
+ public function m() {
+ parent::m();
+ }
+ public function n() {
+ self::n();
+ }
+}
+function
--- /dev/null
+<?php
+/**
+ * Unit test class for the LowerCaseKeyword 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\Standards\Generic\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LowerCaseKeywordUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 10 => 3,
+ 11 => 4,
+ 12 => 1,
+ 13 => 3,
+ 14 => 7,
+ 15 => 1,
+ 16 => 1,
+ 19 => 1,
+ 20 => 1,
+ 21 => 1,
+ 25 => 1,
+ 28 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * @see something
+ */
+if (@in_array($array, $needle))
+{
+ echo '@';
+}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the NoSilencedErrors 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\Standards\Generic\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class NoSilencedErrorsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(5 => 1);
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+if (php_sapi_name() !== 'cli') {}
+if (PHP_SAPI !== 'cli') {}
+if ($object->php_sapi_name() === true) {}
--- /dev/null
+<?php
+/**
+ * Unit test class for the SAPIUsage 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\Standards\Generic\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class SAPIUsageUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(2 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+while {
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the Syntax sniff.
+ *
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @author Blaine Schmeisser <blainesch@gmail.com>
+ * @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\Standards\Generic\Tests\PHP;
+
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class SyntaxUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Should this test be skipped for some reason.
+ *
+ * @return void
+ */
+ protected function shouldSkipTest()
+ {
+ if (defined('PHP_BINARY') === true) {
+ return false;
+ }
+
+ $phpPath = Config::getExecutablePath('php');
+ if ($phpPath === null) {
+ return true;
+ }
+
+ return false;
+
+ }//end shouldSkipTest()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(3 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// True
+function myFunction($arg1, $arg2=TRUE)
+{
+}
+function myFunction($arg1, $arg2=true)
+{
+}
+function myFunction($arg1, $arg2=True)
+{
+}
+
+if ($variable === TRUE) { }
+if ($variable === true) { }
+if ($variable === True) { }
+
+
+// False
+function myFunction($arg1, $arg2=FALSE)
+{
+}
+function myFunction($arg1, $arg2=false)
+{
+}
+function myFunction($arg1, $arg2=False)
+{
+}
+
+if ($variable === FALSE) { }
+if ($variable === false) { }
+if ($variable === False) { }
+
+
+// Null
+function myFunction($arg1, $arg2=NULL)
+{
+}
+function myFunction($arg1, $arg2=null)
+{
+}
+function myFunction($arg1, $arg2=Null)
+{
+}
+
+if ($variable === NULL) { }
+if ($variable === null) { }
+if ($variable === Null) { }
+
+$x = new stdClass();
+$x->null = 7;
+
+use Zend\Log\Writer\Null as NullWriter;
+new \Zend\Log\Writer\Null()
+
+namespace False;
+
+class True extends Null implements False {}
+
+use True\Something;
+use Something\True;
+class MyClass
+{
+ public function myFunction()
+ {
+ $var = array('foo' => new True());
+ }
+}
+
+$x = $f?false:TRUE;
+$x = $f? false:TRUE;
+
+class MyClass
+{
+ // Spice things up a little.
+ const true = FALSE;
+}
+
+var_dump(MyClass::true);
+
+function true() {}
\ No newline at end of file
--- /dev/null
+<?php
+
+// True
+function myFunction($arg1, $arg2=TRUE)
+{
+}
+function myFunction($arg1, $arg2=TRUE)
+{
+}
+function myFunction($arg1, $arg2=TRUE)
+{
+}
+
+if ($variable === TRUE) { }
+if ($variable === TRUE) { }
+if ($variable === TRUE) { }
+
+
+// False
+function myFunction($arg1, $arg2=FALSE)
+{
+}
+function myFunction($arg1, $arg2=FALSE)
+{
+}
+function myFunction($arg1, $arg2=FALSE)
+{
+}
+
+if ($variable === FALSE) { }
+if ($variable === FALSE) { }
+if ($variable === FALSE) { }
+
+
+// Null
+function myFunction($arg1, $arg2=NULL)
+{
+}
+function myFunction($arg1, $arg2=NULL)
+{
+}
+function myFunction($arg1, $arg2=NULL)
+{
+}
+
+if ($variable === NULL) { }
+if ($variable === NULL) { }
+if ($variable === NULL) { }
+
+$x = new stdClass();
+$x->null = 7;
+
+use Zend\Log\Writer\Null as NullWriter;
+new \Zend\Log\Writer\Null()
+
+namespace False;
+
+class True extends Null implements False {}
+
+use True\Something;
+use Something\True;
+class MyClass
+{
+ public function myFunction()
+ {
+ $var = array('foo' => new True());
+ }
+}
+
+$x = $f?FALSE:TRUE;
+$x = $f? FALSE:TRUE;
+
+class MyClass
+{
+ // Spice things up a little.
+ const true = FALSE;
+}
+
+var_dump(MyClass::true);
+
+function true() {}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the UpperCaseConstant 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\Standards\Generic\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class UpperCaseConstantUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 7 => 1,
+ 10 => 1,
+ 15 => 1,
+ 16 => 1,
+ 23 => 1,
+ 26 => 1,
+ 31 => 1,
+ 32 => 1,
+ 39 => 1,
+ 42 => 1,
+ 47 => 1,
+ 48 => 1,
+ 70 => 1,
+ 71 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$x = 'My '.'string';
+$x = 'My '.1234;
+$x = 'My '.$y.' test';
+
+echo $data['my'.'index'];
+echo $data['my'.4];
+echo $data['my'.$x];
+echo $data[$x.$y.'My'.'String'];
+
+$code = '$actions = array();'."\n";
+$code = "$actions = array();"."\n";
+
+// No errors for these because they are needed in some cases.
+$code = ' ?'.'>';
+$code = '<'.'?php ';
+
+$string = 'This is a really long string. '
+ . 'It is being used for errors. '
+ . 'The message is not translated.';
+?>
--- /dev/null
+var x = 'My ' + 'string';
+var x = 'My ' + 1234;
+var x = 'My ' + y + ' test';
+
+this.errors['test'] = x;
+this.errors['test' + 10] = x;
+this.errors['test' + y] = x;
+this.errors['test' + 'blah'] = x;
+this.errors[y] = x;
+this.errors[y + z] = x;
+this.errors[y + z + 'My' + 'String'] = x;
+
+var long = 'This is a really long string. '
+ + 'It is being used for errors. '
+ + 'The message is not translated.';
--- /dev/null
+<?php
+/**
+ * Unit test class for the UnnecessaryStringConcat 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\Standards\Generic\Tests\Strings;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class UnnecessaryStringConcatUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='UnnecessaryStringConcatUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'UnnecessaryStringConcatUnitTest.inc':
+ return array(
+ 2 => 1,
+ 6 => 1,
+ 9 => 1,
+ 12 => 1,
+ 19 => 1,
+ 20 => 1,
+ );
+ break;
+ case 'UnnecessaryStringConcatUnitTest.js':
+ return array(
+ 1 => 1,
+ 8 => 1,
+ 11 => 1,
+ 14 => 1,
+ 15 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+#login-container {
+ margin-left: -225px;
+ width: 450px;
+}
--- /dev/null
+#login-container {
+ margin-left: -225px;
+ width: 450px;
+}
--- /dev/null
+<?php
+
+class ExampleClass
+{
+ function exampleFunction() {}
+
+}
+
+ $o = <<<EOF
+ this is some text
+ this is some text
+EOF;
+
+ $correctVar = false;
+ $correctVar = true;
+
+// Indent with spaces is not allowed
+ $hello = array();
+ $world = '';
+// here the indention is mixed with tabs and spaces
+// [tab][space][space][space][tab]return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
+ return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
+// [space][space][space][tab]return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
+ return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
+// Doc comments are indent with tabs and one space
+//[tab]/**
+//[tab][space]*
+ /**
+ * CVS revision for HTTP headers.
+ *
+ * @var string
+ * @access private
+ */
+ /**
+ *
+ */
+
+$str = 'hello
+ there';
+
+/**
+ * This PHP DocBlock should be fine, even though there is a single space at the beginning.
+ *
+ * @var int $x
+ */
+$x = 1;
+
+?>
+<html>
+ <head>
+ <title>Foo</title>
+ </head>
+ <body>
+ <div>
+ <div>
+ <div>
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
+
+<?php
+
+ // Issue #1404
+ // This is a line with mixed tabs and spaces.
+ echo 'And here is another line with mixed tabs and spaces.'
+ echo 'And another one.'
+ echo 'And another one.'
+ echo 'And another one.'
+
+// Spaces after comment.
+
+$x = 1;
+
+// Mixed tabs and spaces after comment.
+
+$x = 1;
+
+// Mixed spaces and tabs after comment.
+
+$x = 1;
--- /dev/null
+<?php
+
+class ExampleClass
+{
+ function exampleFunction() {}
+
+}
+
+ $o = <<<EOF
+ this is some text
+ this is some text
+EOF;
+
+ $correctVar = false;
+ $correctVar = true;
+
+// Indent with spaces is not allowed
+ $hello = array();
+ $world = '';
+// here the indention is mixed with tabs and spaces
+// [tab][space][space][space][tab]return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
+ return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
+// [space][space][space][tab]return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
+ return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
+// Doc comments are indent with tabs and one space
+//[tab]/**
+//[tab][space]*
+ /**
+ * CVS revision for HTTP headers.
+ *
+ * @var string
+ * @access private
+ */
+ /**
+ *
+ */
+
+$str = 'hello
+ there';
+
+/**
+ * This PHP DocBlock should be fine, even though there is a single space at the beginning.
+ *
+ * @var int $x
+ */
+$x = 1;
+
+?>
+<html>
+ <head>
+ <title>Foo</title>
+ </head>
+ <body>
+ <div>
+ <div>
+ <div>
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
+
+<?php
+
+ // Issue #1404
+ // This is a line with mixed tabs and spaces.
+ echo 'And here is another line with mixed tabs and spaces.'
+ echo 'And another one.'
+ echo 'And another one.'
+ echo 'And another one.'
+
+// Spaces after comment.
+
+$x = 1;
+
+// Mixed tabs and spaces after comment.
+
+$x = 1;
+
+// Mixed spaces and tabs after comment.
+
+$x = 1;
--- /dev/null
+var x = {
+ abc: 1,
+ zyz: 2,
+ abc: 5,
+ mno: {
+ abc: 4
+ },
+ abc: 5
+}
--- /dev/null
+var x = {
+ abc: 1,
+ zyz: 2,
+ abc: 5,
+ mno: {
+ abc: 4
+ },
+ abc: 5
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowSpaceIndent 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\Standards\Generic\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowSpaceIndentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='DisallowSpaceIndentUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'DisallowSpaceIndentUnitTest.inc':
+ return array(
+ 5 => 1,
+ 9 => 1,
+ 15 => 1,
+ 22 => 1,
+ 24 => 1,
+ 30 => 1,
+ 35 => 1,
+ 50 => 1,
+ 55 => 1,
+ 57 => 1,
+ 58 => 1,
+ 59 => 1,
+ 60 => 1,
+ 65 => 1,
+ 66 => 1,
+ 67 => 1,
+ 68 => 1,
+ 69 => 1,
+ 70 => 1,
+ 73 => 1,
+ 77 => 1,
+ 81 => 1,
+ );
+ break;
+ case 'DisallowSpaceIndentUnitTest.js':
+ return array(3 => 1);
+ break;
+ case 'DisallowSpaceIndentUnitTest.css':
+ return array(2 => 1);
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+#login-container {
+ margin-left: -225px;
+ width: 450px;
+}
+
--- /dev/null
+#login-container {
+ margin-left: -225px;
+ width: 450px;
+}
+
--- /dev/null
+<?php
+
+class ExampleClass
+{
+ function exampleFunction() {}
+
+}
+
+ $o = <<<EOF
+ this is some text
+ this is some text
+EOF;
+
+ $correctVar = true;
+ $correctVar = false;
+
+class MyClass
+{
+ /**
+ * Short description.
+ *
+ * @return void
+ */
+ public function myFunction() { }
+}
+
+$str = 'hello
+ there';
+
+$foo = array(
+ 'Česká republika' => 'Czech republic',
+ 'România' => 'Romania',
+ 'Magyarország' => 'Hungary',
+);
+
+$var = "$hello $there";
+
+?>
+<html>
+ <head>
+ <title>Foo</title>
+ </head>
+ <body>
+ <div>
+ <div>
+ <div>
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
--- /dev/null
+<?php
+
+class ExampleClass
+{
+ function exampleFunction() {}
+
+}
+
+ $o = <<<EOF
+ this is some text
+ this is some text
+EOF;
+
+ $correctVar = true;
+ $correctVar = false;
+
+class MyClass
+{
+ /**
+ * Short description.
+ *
+ * @return void
+ */
+ public function myFunction() { }
+}
+
+$str = 'hello
+ there';
+
+$foo = array(
+ 'Česká republika' => 'Czech republic',
+ 'România' => 'Romania',
+ 'Magyarország' => 'Hungary',
+);
+
+$var = "$hello $there";
+
+?>
+<html>
+ <head>
+ <title>Foo</title>
+ </head>
+ <body>
+ <div>
+ <div>
+ <div>
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
--- /dev/null
+var x = {
+ abc: 1,
+ zyz: 2,
+ abc: 5,
+ mno: {
+ abc: 4
+ },
+ abc: 5
+}
--- /dev/null
+var x = {
+ abc: 1,
+ zyz: 2,
+ abc: 5,
+ mno: {
+ abc: 4
+ },
+ abc: 5
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowTabIndent 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\Standards\Generic\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowTabIndentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Get a list of CLI values to set before the file is tested.
+ *
+ * @param string $testFile The name of the file being tested.
+ * @param \PHP_CodeSniffer\Config $config The config data for the test run.
+ *
+ * @return void
+ */
+ public function setCliValues($testFile, $config)
+ {
+ $config->tabWidth = 4;
+
+ }//end setCliValues()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='DisallowTabIndentUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'DisallowTabIndentUnitTest.inc':
+ return array(
+ 5 => 2,
+ 9 => 1,
+ 15 => 1,
+ 20 => 2,
+ 21 => 1,
+ 22 => 2,
+ 23 => 1,
+ 24 => 2,
+ 31 => 1,
+ 32 => 2,
+ 33 => 2,
+ 41 => 1,
+ 42 => 1,
+ 43 => 1,
+ 44 => 1,
+ 45 => 1,
+ 46 => 1,
+ 47 => 1,
+ 48 => 1,
+ );
+ break;
+ case 'DisallowTabIndentUnitTest.js':
+ return array(
+ 3 => 1,
+ 5 => 1,
+ 6 => 1,
+ );
+ break;
+ case 'DisallowTabIndentUnitTest.css':
+ return array(
+ 1 => 1,
+ 2 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+@codingStandardsChangeSetting Generic.WhiteSpace.ScopeIndent tabIndent false
+<meta name="twitter:site" content="<?php echo $metaTagsData['twitter:site']; ?>">
+<?php
+class Test {
+ function __construct()
+ {
+ $this->hello();
+ }
+
+ function hello()
+ {
+ echo 'hello';
+}//end hello()
+
+ function hello2()
+ {
+ if (TRUE) {
+ echo 'hello'; // no error here as its more than 4 spaces.
+ } else {
+ echo 'bye';
+ }
+
+ while (TRUE) {
+ echo 'hello';
+ }
+
+ do {
+ echo 'hello';
+ } while (TRUE);
+ }
+
+ function hello3()
+ {
+ switch ($hello) {
+ case 'hello':
+ break;
+ }
+ }
+
+}
+
+?>
+<pre>
+</head>
+<body>
+<?php
+if ($form->validate()) {
+ $safe = $form->getSubmitValues();
+}
+?>
+</pre>
+<?php
+
+class Test2
+{
+ function __construct()
+ {
+ // $this->open(); // error here
+ }
+
+ public function open()
+ {
+ // Some inline stuff that shouldn't error
+ if (TRUE) echo 'hello';
+ foreach ($tokens as $token) echo $token;
+ }
+
+ /**
+ * This is a comment 1.
+ * This is a comment 2.
+ * This is a comment 3.
+ * This is a comment 4.
+ */
+ public function close()
+ {
+ // All ok.
+ if (TRUE) {
+ if (TRUE) {
+ } else if (FALSE) {
+ foreach ($tokens as $token) {
+ switch ($token) {
+ case '1':
+ case '2':
+ if (true) {
+ if (false) {
+ if (false) {
+ if (false) {
+ echo 'hello';
+ }
+ }
+ }
+ }
+ break;
+ case '5':
+ break;
+ }
+ do {
+ while (true) {
+ foreach ($tokens as $token) {
+ for ($i = 0; $i < $token; $i++) {
+ echo 'hello';
+ }
+ }
+ }
+ } while (true);
+ }
+ }
+ }
+ }
+
+ /*
+ This is another c style comment 1.
+ This is another c style comment 2.
+ This is another c style comment 3.
+ This is another c style comment 4.
+ This is another c style comment 5.
+ */
+
+ /* This is a T_COMMENT
+ *
+ *
+ *
+ */
+
+ /** This is a T_DOC_COMMENT
+ */
+
+ /*
+ This T_COMMENT has a newline in it.
+
+ */
+
+ public function read()
+ {
+ echo 'hello';
+
+ // no errors below.
+ $array = array(
+ 'this',
+ 'that' => array(
+ 'hello',
+ 'hello again' => array(
+ 'hello',
+ ),
+ ),
+ );
+ }
+}
+
+abstract class Test3
+{
+ public function parse()
+ {
+
+ foreach ($t as $ndx => $token) {
+ if (is_array($token)) {
+ echo 'here';
+ } else {
+ $ts[] = array("token" => $token, "value" => '');
+
+ $last = count($ts) - 1;
+
+ switch ($token) {
+ case '(':
+
+ if ($last >= 3 &&
+ $ts[0]['token'] != T_CLASS &&
+ $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
+ $ts[$last - 3]['token'] == T_VARIABLE ) {
+
+
+ if (true) {
+ echo 'hello';
+ }
+ }
+ array_push($braces, $token);
+ break;
+ }
+ }
+ }
+ }
+}
+
+public function test()
+{
+ $o = <<<EOF
+this is some text
+this is some text
+this is some text
+this is some text
+this is some text
+this is some text
+EOF;
+
+ return $o;
+}
+
+if ($a === true || $a === true || $a === true || $a === true ||
+ $a === true || $a === true || $a === true || $a === true) {
+
+ echo 'hello';
+}
+
+if ($true) {
+ /* First comment line
+ *
+ * Comment test here
+ * Comment test here
+ *
+ */
+
+ /* First comment line
+ *
+ * Comment test here
+ * Comment test here
+ *
+ this si something */
+}
+
+function test()
+{
+ /* taken from http://de3.php.net/manual/en/reserved.php */
+ # $m[] = 'declare';
+ /* taken from http://de3.php.net/manual/en/reserved.php */
+ # $m[] = 'declare';
+}
+
+foreach ($elements as $element) {
+ if ($something) {
+ // Do IF.
+ } else if ($somethingElse) {
+ // Do ELSE.
+ }
+}
+
+if ($condition) {
+ echo "This is a long
+string that spans $numLines lines
+without indenting.
+";
+}
+
+if ($condition) {
+ echo 'This is a long
+ string that spans multiple lines
+ with indenting.
+ ';
+}
+
+if ($condition) {
+ echo 'This is a long
+ string that spans multiple lines
+ with indenting.';
+}
+
+switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ echo $string{1};
+ }
+ break;
+}
+
+function temp($foo, $bar) {
+ switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ return $foo;
+ }
+ break;
+ }
+}
+
+switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ if ($something) {
+ echo $string{1};
+ } else if ($else) {
+ switch ($else) {
+ default:
+ }
+ }
+ }
+ break;
+}
+
+switch ($name) {
+ case "1":
+ case "2":
+ case "3":
+ return true;
+}
+
+switch ($name) {
+ case "1":
+ case "2":
+ case "3":
+ default :
+ return true;
+}
+
+function myFunction()
+{
+ ?>
+ <dynamic_content>
+
+ </dynamic_content>
+ <?php
+
+}
+
+switch ($name) {
+ case "1":
+ switch ($name2) {
+ case "1":
+ break;
+ case "2":
+ break;
+ }
+ break;
+ case "2":
+ break;
+}
+
+switch (true) {
+ case true: {
+ }
+ echo 'hi';
+ break;
+ case false:
+ case null:{
+ echo 'hi';
+ }
+ break;
+}
+
+switch (true) {
+ case true: {
+ echo 'hi';
+ }
+ // No break here.
+ case false:
+ case true:
+ case null:{
+ echo 'hi';
+ echo 'hi';
+ }
+ break;
+}
+
+switch (true) {
+ case true: {
+ if (true) {
+ echo 'hi';
+ }
+ }
+ break;
+}
+
+// Testing anon function.
+class a {
+ function c()
+ {
+ $this->doSomething(
+ function () {
+ echo 123;
+ }
+ );
+ }
+}
+
+some_function(
+ function() {
+ $a = 403;
+ if ($a === 404) {
+ $a = 403;
+ }
+ }
+);
+
+some_function(
+ function() {
+ $a = 403;
+ if ($a === 404) {
+ $a = 403;
+ }
+ }
+);
+
+$myFunction = function() {
+ $a = 403;
+ if ($a === 404) {
+ $a = 403;
+ }
+}
+
+class Whatever
+{
+ protected $_protectedArray = array(
+ 'normalString' => 'That email address is already in use!',
+ 'offendingString' => <<<'STRING'
+Each line of this string is always said to be at column 0,
+ no matter how many spaces are placed
+ at the beginning of each line
+and the ending STRING on the next line is reported as having to be indented.
+STRING
+ );
+}
+
+class MyClass
+{
+ public static function myFunction()
+ {
+ if (empty($keywords) === FALSE) {
+ $keywords = 'foo';
+ $existing = 'foo';
+ }
+
+ return $keywords;
+
+ }//end myFunction()
+
+}//end class
+
+$var = call_user_func(
+ $new_var = function () use (&$a) {
+ if ($a > 0) {
+ return $a++;
+ } else {
+ return $a--;
+ }
+ }
+);
+
+class AnonymousFn
+{
+ public function getAnonFn()
+ {
+ return array(
+ 'functions' => Array(
+ 'function1' => function ($a, $b, $c) {
+ $a = $b + $c;
+ $b = $c / 2;
+ return Array($a, $b, $c);
+ },
+ ),
+ );
+ }
+}
+?>
+
+<div>
+ <?php
+ if ($myvar == 'test') {
+ echo 'something';
+ }
+ ?>
+<div>
+<div>
+ <?php
+ if ($myvar == 'test') {
+ echo 'something';
+ }
+ ?>
+<div>
+<div>
+ <?php
+if ($myvar == 'test') {
+ echo 'something';
+}
+ ?>
+<div>
+<body>
+ <?php
+ if (isset($_GET["test"])) {
+ if ($_GET["test"] <> "") {
+ $test = true;
+ } else {
+ $test = true;
+ }
+ }
+ ?>
+</body>
+
+<?php
+if (true) {
+ ?>
+ <div>
+ <div>
+ <div>
+ <?php
+ if (true) {
+ echo 'hello';
+ } else {
+ echo 'goodbye';
+ }
+ ?>
+ </div>
+ </div>
+ </div>
+ <?php
+} else {
+ echo 'else';
+}
+?>
+<?php if (isset($param)) { ?>
+ <h3>some text</h3>
+<?php }
+
+$list = [
+ 'fn' => function ($a) {
+ if ($a === true) {
+ echo 'hi';
+ }
+ }
+];
+
+$list = [
+ 'fn' => function ($a) {
+ if ($a === true) {
+ echo 'hi';
+ }
+ }
+];
+
+if ($foo) {
+ foreach ($bar as $baz) {
+ if ($baz) {
+ ?>
+ <div>
+ <div>
+ <div>
+ <?php
+ if ($baz > 1) {
+ echo '1';
+ }
+ ?>
+ </div>
+ <?php
+ if ($baz > 1) {
+ echo '1';
+ }
+ ?>
+ </div>
+ <?php
+ if ($baz > 1) {
+ echo '1';
+ }
+ ?>
+ </div>
+ <?php
+ }
+ }
+}
+
+?>
+<title><?= CHtml::encode($this->pageTitle); ?></title>
+
+<?php
+if ($foo) {
+ echo '1';
+ echo '2';
+ echo '3';
+}
+
+$array = array();
+array_map(
+ function($x)
+ {
+ return trim($x);
+ },
+ $array
+);
+
+function foo(){return function(){};}
+
+$mockedDatabase->expects($this->at(2))
+ ->with($this->callback(
+ function ($subject)
+ {
+ }
+ )
+ );
+
+/** @var Database $mockedDatabase */
+/** @var Container $mockedContainer */
+
+echo $string->append('foo')
+ ->appaend('bar')
+ ->appaend('baz')
+ ->outputUsing(
+ function ()
+ {
+ }
+ );
+
+echo PHP_EOL;
+
+switch ($arg) {
+ case 1:
+ break;
+ case 2:
+ if ($arg2 == 'foo') {
+ }
+ case 3:
+ default:
+ echo 'default';
+}
+
+if ($tokens[$stackPtr]['content']{0} === '#') {
+} else if ($tokens[$stackPtr]['content']{0} === '/'
+ && $tokens[$stackPtr]['content']{1} === '/'
+) {
+}
+
+$var = call_user_func(
+ function() {
+ if ($foo) {
+ $new_var = function() {
+ if ($a > 0) {
+ return $a++;
+ } else {
+ return $a--;
+ }
+ }
+ }
+ }
+);
+
+a(
+ function() {
+ $a = function() {
+ $b = false;
+ };
+ true
+ }
+);
+
+$var = [
+ [
+ '1' =>
+ function () {
+ return true;
+ },
+ ],
+ [
+ '1' =>
+ function () {
+ return true;
+ },
+ '2' => true,
+ ]
+];
+
+if ($foo) {
+ ?>
+ </p>
+ <?php
+}
+
+function foo()
+{
+ $failingTests[$testName][] = array(
+ 'comment' => self::_replaceKeywords($failingComment, $result),
+ 'screenshot' => Test::getScreenshotPath(
+ $projectid,
+ $result['class_name'],
+ ),
+ );
+
+}
+
+$this->mockedDatabase
+ ->with(
+ $this->callback(
+ function () {
+ return;
+ }
+ )
+ );
+
+$this->subject->recordLogin();
+
+function a()
+{
+ if (true) {
+ static::$a[$b] =
+ static::where($c)
+ ->where($c)
+ ->where(
+ function ($d) {
+ $d->whereNull();
+ $d->orWhere();
+ }
+ )
+ ->first();
+
+ if (static::$a[$b] === null) {
+ static::$a[$b] = new static(
+ array(
+ 'a' => $a->id,
+ 'a' => $a->id,
+ )
+ );
+ }
+ }
+
+ return static::$a[$b];
+}
+
+$foo->load(
+ array(
+ 'bar' => function ($baz) {
+ $baz->call();
+ }
+ )
+);
+
+hello();
+
+$foo = array_unique(
+ array_map(
+ function ($entry) {
+ return $entry * 2;
+ },
+ array()
+ )
+);
+bar($foo);
+
+class PHP_CodeSniffer_Tokenizers_JS
+{
+
+ public $scopeOpeners = array(
+ T_CASE => array(
+ 'end' => array(
+ T_BREAK => T_BREAK,
+ T_RETURN => T_RETURN,
+ ),
+ 'strict' => true,
+ ),
+ );
+}
+
+echo $string->
+ append('foo')->
+ appaend('bar')->
+ appaend('baz')->
+ outputUsing(
+ function ()
+ {
+ }
+ );
+
+$str = 'the items I want to show are: ' .
+ implode(
+ ', ',
+ array('a', 'b', 'c')
+ );
+
+echo $str;
+
+$str = 'foo'
+ . '1'
+ . '2';
+
+echo $str;
+
+bar([
+ 'foo' => foo(function () {
+ return 'foo';
+ })
+]);
+
+$domains = array_unique(
+ array_map(
+ function ($url) {
+ $urlObject = new \Purl\Url($url);
+ return $urlObject->registerableDomain;
+ },
+ $sites
+ )
+);
+
+return $domains;
+
+if ($a == 5) :
+ echo "a equals 5";
+ echo "...";
+elseif ($a == 6) :
+ echo "a equals 6";
+ echo "!!!";
+else :
+ echo "a is neither 5 nor 6";
+endif;
+
+if ($foo):
+if ($bar) $foo = 1;
+elseif ($baz) $foo = 2;
+endif;
+
+$this
+ ->method(array(
+ 'foo' => 'bar',
+ ), 'arg', array(
+ 'foo' => 'bar',
+ ));
+
+class Foo
+{
+ use Bar {
+ myMethod as renamedMethod;
+ }
+}
+
+class Foo
+{
+ use Bar {
+ myMethod as renamedMethod;
+ }
+}
+
+foo();
+
+array(
+ 'key1' => function ($bar) {
+ return $bar;
+ },
+ 'key2' => function ($foo) {
+ return $foo;
+ },
+);
+
+?>
+<script>
+ var foo = <?php echo bar(
+ 'baz'
+ ); ?>;
+</script>
+<?php
+
+try {
+ echo 'foo';
+} catch (\Exception $e) {
+ echo 'catch';
+} finally {
+ if (false) {
+ echo 'finally false';
+ }
+}
+
+class C0
+{
+ public function m0()
+ {
+ }
+
+ public function m1()
+ {
+ }
+}
+
+namespace Foo {
+
+ use \Foo\Bar;
+ use \Foo\Baz;
+
+ function test() {
+ return true;
+ }
+
+}
+
+declare(ticks=1) {
+echo 'foo';
+}
+
+$a =
+ (
+ $b = 1
+ );
+$c = 2;
+
+$a =
+ [
+ $b => 1,
+ ];
+$c = 2;
+
+class foo
+{
+ public function get()
+ {
+ $foo = ['b' => 'c',
+ 'd' => [
+ ['e' => 'f']
+ ]];
+ echo '42';
+
+ $foo = array('b' => 'c',
+ 'd' => array(
+ array('e' => 'f')
+ ));
+ echo '42';
+ }
+}
+
+switch ($foo) {
+ case 1:
+ return array();
+ case 2:
+ return '';
+ case 3:
+ return $function();
+ case 4:
+ return $functionCall($param[0]);
+ case 5:
+ return array() + array(); // Array Merge
+ case 6:
+ // String connect
+ return $functionReturningString('') . $functionReturningString(array());
+ case 7:
+ return functionCall(
+ $withMultiLineParam[0],
+ array(),
+ $functionReturningString(
+ $withMultiLineParam[1]
+ )
+ );
+ case 8:
+ return $param[0][0];
+}
+
+class Test {
+
+ public
+ $foo
+ ,$bar
+ ,$baz = [ ]
+ ;
+
+ public function wtfindent() {
+ }
+}
+
+switch ($x) {
+ case 1:
+ return [1];
+ default:
+ return [2];
+}
+
+switch ($foo) {
+ case self::FOO:
+ return $this->bar($gfoo, function ($id) {
+ return FOO::bar($id);
+ }, $values);
+ case self::BAR:
+ $values = $this->bar($foo, $values);
+ break;
+}
+
+$var = array(
+ 'long description' =>
+ array(0, 'something'),
+ 'another long description' =>
+ array(1, "something else")
+);
+
+$services = array(
+ 'service 1' =>
+ Mockery::mock('class 1')
+ ->shouldReceive('setFilter')->once()
+ ->shouldReceive('getNbResults')->atLeast()->once()
+ ->shouldReceive('getSlice')->once()->andReturn(array())
+ ->getMock(),
+ 'service 2' =>
+ Mockery::mock('class 2')
+ ->shouldReceive('__invoke')->once()
+ ->getMock()
+);
+
+class Foo
+{
+ public function setUp()
+ {
+ $this->foo = new class {
+ public $name = 'Some value';
+ };
+ }
+}
+
+try {
+ foo();
+} catch (\Exception $e) {
+ $foo = function() {
+ return 'foo';
+ }
+
+ if (true) {
+ }
+}
+
+if ($foo) {
+ foo();
+} else if (\Exception $e) {
+ $foo = function() {
+ return 'foo';
+ }
+
+ if (true) {
+ }
+} else {
+ $foo = function() {
+ return 'foo';
+ }
+
+ if (true) {
+ }
+}
+
+switch ($parameter) {
+ case null:
+ return [
+ 'foo' => in_array(
+ 'foo',
+ []
+ ),
+ ];
+
+ default:
+ return [];
+}
+
+class SomeClass
+{
+ public function someFunc()
+ {
+ a(function () {
+ echo "a";
+ })->b(function () {
+ echo "b";
+ });
+
+ if (true) {
+ echo "c";
+ }
+ echo "d";
+ }
+}
+
+$params = self::validate_parameters(self::read_competency_framework_parameters(),
+ array(
+ 'id' => $id,
+ ));
+
+$framework = api::read_framework($params['id']);
+self::validate_context($framework->get_context());
+$output = $PAGE->get_renderer('tool_lp');
+
+class Test123
+{
+ protected static
+ $prop1 = [
+ 'testA' => 123,
+ ],
+ $prop2 = [
+ 'testB' => 456,
+ ],
+
+ protected static
+ $prop3 = array(
+ 'testA' => 123,
+ ),
+ $prop4 = array(
+ 'testB' => 456,
+ ),
+
+ protected static $prop5;
+}
+
+$foo = foo(
+ function () {
+ $foo->debug(
+ $a,
+ $b
+ );
+
+ if ($a) {
+ $b = $a;
+ }
+ }
+);
+
+if (somethingIsTrue()) {
+ ?>
+ <div>
+ <?php
+ $someVar = doSomething(
+ 'foobar',
+ true,
+ 'foo',
+ 'bar'
+ );
+
+ if (somethingElseIsTrue()) {
+ doOneThing();
+ } else {
+ doTheOtherThing();
+ }
+ ?>
+ </div>
+ <?php
+}
+
+$foo = [
+ new class() implements Bar {
+
+ public static function foo(): string
+ {
+ return 'foo';
+ }
+ },
+];
+
+$foo = [
+ function () {
+ if ($foo) {
+ return 'foo';
+ }
+ },
+];
+
+$foo
+ ->bar(foo(function () {
+ }), foo(function () {
+ }));
+
+echo 'foo';
+
+class Test {
+
+ public function a() {
+ ?>a<?php
+ }
+
+ public function b(
+ $a
+ ) {
+ echo $a;
+ }
+}
+
+$foo = foo(
+ function () {
+ $foo->debug(
+ $a,
+ $b
+ );
+
+ if ($a) {
+ $b = $a;
+ }
+ }
+);
+
+function test()
+{
+ $array = [];
+ foreach ($array as $data) {
+ [
+ 'key1' => $var1,
+ 'key2' => $var2,
+ ] = $data;
+ foreach ($var1 as $method) {
+ echo $method . $var2;
+ }
+ }
+}
+
+switch ($a) {
+ case 0:
+ function () {
+ };
+ case 1:
+ break;
+}
+
+class Test
+{
+ public function __construct()
+ {
+if (false) {
+echo 0;
+ }
+ }
+}
+
+return [
+ 'veryLongKeySoIWantToMakeALineBreak'
+ => 'veryLonValueSoIWantToMakeALineBreak',
+
+ 'someOtherKey' => [
+ 'someValue'
+ ],
+
+ 'arrayWithArraysInThere' => [
+ ['Value1', 'Value1']
+ ],
+];
+
+switch ($sContext) {
+ case 'SOMETHING':
+ case 'CONSTANT':
+ do_something();
+ break;
+ case 'GLOBAL':
+ case 'GLOBAL1':
+ do_something();
+ // Fall through
+ default:
+ {
+ do_something();
+ }
+}
+
+public function foo()
+{
+ $foo('some
+ long description', function () {
+ });
+
+ $foo('some
+ long
+ description', function () {
+ });
+
+ $foo(
+'some long description', function () {
+ });
+}
+
+ function foo()
+ {
+ $foo = array(
+ );
+
+ if ($foo) {
+ echo 'foo';
+ }
+
+ return false;
+ }
+
+?>
+<?php
+ if (true) {
+ }
+ else if (true) {
+ }
+
+$a = 1;
+
+/*
+$a = array(
+ */
+);
+
+echo ""
--- /dev/null
+@codingStandardsChangeSetting Generic.WhiteSpace.ScopeIndent tabIndent false
+<meta name="twitter:site" content="<?php echo $metaTagsData['twitter:site']; ?>">
+<?php
+class Test {
+ function __construct()
+ {
+ $this->hello();
+ }
+
+ function hello()
+ {
+ echo 'hello';
+ }//end hello()
+
+ function hello2()
+ {
+ if (TRUE) {
+ echo 'hello'; // no error here as its more than 4 spaces.
+ } else {
+ echo 'bye';
+ }
+
+ while (TRUE) {
+ echo 'hello';
+ }
+
+ do {
+ echo 'hello';
+ } while (TRUE);
+ }
+
+ function hello3()
+ {
+ switch ($hello) {
+ case 'hello':
+ break;
+ }
+ }
+
+}
+
+?>
+<pre>
+</head>
+<body>
+<?php
+if ($form->validate()) {
+ $safe = $form->getSubmitValues();
+}
+?>
+</pre>
+<?php
+
+class Test2
+{
+ function __construct()
+ {
+ // $this->open(); // error here
+ }
+
+ public function open()
+ {
+ // Some inline stuff that shouldn't error
+ if (TRUE) echo 'hello';
+ foreach ($tokens as $token) echo $token;
+ }
+
+ /**
+ * This is a comment 1.
+ * This is a comment 2.
+ * This is a comment 3.
+ * This is a comment 4.
+ */
+ public function close()
+ {
+ // All ok.
+ if (TRUE) {
+ if (TRUE) {
+ } else if (FALSE) {
+ foreach ($tokens as $token) {
+ switch ($token) {
+ case '1':
+ case '2':
+ if (true) {
+ if (false) {
+ if (false) {
+ if (false) {
+ echo 'hello';
+ }
+ }
+ }
+ }
+ break;
+ case '5':
+ break;
+ }
+ do {
+ while (true) {
+ foreach ($tokens as $token) {
+ for ($i = 0; $i < $token; $i++) {
+ echo 'hello';
+ }
+ }
+ }
+ } while (true);
+ }
+ }
+ }
+ }
+
+ /*
+ This is another c style comment 1.
+ This is another c style comment 2.
+ This is another c style comment 3.
+ This is another c style comment 4.
+ This is another c style comment 5.
+ */
+
+ /* This is a T_COMMENT
+ *
+ *
+ *
+ */
+
+ /** This is a T_DOC_COMMENT
+ */
+
+ /*
+ This T_COMMENT has a newline in it.
+
+ */
+
+ public function read()
+ {
+ echo 'hello';
+
+ // no errors below.
+ $array = array(
+ 'this',
+ 'that' => array(
+ 'hello',
+ 'hello again' => array(
+ 'hello',
+ ),
+ ),
+ );
+ }
+}
+
+abstract class Test3
+{
+ public function parse()
+ {
+
+ foreach ($t as $ndx => $token) {
+ if (is_array($token)) {
+ echo 'here';
+ } else {
+ $ts[] = array("token" => $token, "value" => '');
+
+ $last = count($ts) - 1;
+
+ switch ($token) {
+ case '(':
+
+ if ($last >= 3 &&
+ $ts[0]['token'] != T_CLASS &&
+ $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
+ $ts[$last - 3]['token'] == T_VARIABLE ) {
+
+
+ if (true) {
+ echo 'hello';
+ }
+ }
+ array_push($braces, $token);
+ break;
+ }
+ }
+ }
+ }
+}
+
+public function test()
+{
+ $o = <<<EOF
+this is some text
+this is some text
+this is some text
+this is some text
+this is some text
+this is some text
+EOF;
+
+ return $o;
+}
+
+if ($a === true || $a === true || $a === true || $a === true ||
+ $a === true || $a === true || $a === true || $a === true) {
+
+ echo 'hello';
+}
+
+if ($true) {
+ /* First comment line
+ *
+ * Comment test here
+ * Comment test here
+ *
+ */
+
+ /* First comment line
+ *
+ * Comment test here
+ * Comment test here
+ *
+ this si something */
+}
+
+function test()
+{
+ /* taken from http://de3.php.net/manual/en/reserved.php */
+ # $m[] = 'declare';
+ /* taken from http://de3.php.net/manual/en/reserved.php */
+ # $m[] = 'declare';
+}
+
+foreach ($elements as $element) {
+ if ($something) {
+ // Do IF.
+ } else if ($somethingElse) {
+ // Do ELSE.
+ }
+}
+
+if ($condition) {
+ echo "This is a long
+string that spans $numLines lines
+without indenting.
+";
+}
+
+if ($condition) {
+ echo 'This is a long
+ string that spans multiple lines
+ with indenting.
+ ';
+}
+
+if ($condition) {
+ echo 'This is a long
+ string that spans multiple lines
+ with indenting.';
+}
+
+switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ echo $string{1};
+ }
+ break;
+}
+
+function temp($foo, $bar) {
+ switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ return $foo;
+ }
+ break;
+ }
+}
+
+switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ if ($something) {
+ echo $string{1};
+ } else if ($else) {
+ switch ($else) {
+ default:
+ }
+ }
+ }
+ break;
+}
+
+switch ($name) {
+ case "1":
+ case "2":
+ case "3":
+ return true;
+}
+
+switch ($name) {
+ case "1":
+ case "2":
+ case "3":
+ default :
+ return true;
+}
+
+function myFunction()
+{
+ ?>
+ <dynamic_content>
+
+ </dynamic_content>
+ <?php
+
+}
+
+switch ($name) {
+ case "1":
+ switch ($name2) {
+ case "1":
+ break;
+ case "2":
+ break;
+ }
+ break;
+ case "2":
+ break;
+}
+
+switch (true) {
+ case true: {
+ }
+ echo 'hi';
+ break;
+ case false:
+ case null:{
+ echo 'hi';
+ }
+ break;
+}
+
+switch (true) {
+ case true: {
+ echo 'hi';
+ }
+ // No break here.
+ case false:
+ case true:
+ case null:{
+ echo 'hi';
+ echo 'hi';
+ }
+ break;
+}
+
+switch (true) {
+ case true: {
+ if (true) {
+ echo 'hi';
+ }
+ }
+ break;
+}
+
+// Testing anon function.
+class a {
+ function c()
+ {
+ $this->doSomething(
+ function () {
+ echo 123;
+ }
+ );
+ }
+}
+
+some_function(
+ function() {
+ $a = 403;
+ if ($a === 404) {
+ $a = 403;
+ }
+ }
+);
+
+some_function(
+ function() {
+ $a = 403;
+ if ($a === 404) {
+ $a = 403;
+ }
+ }
+);
+
+$myFunction = function() {
+ $a = 403;
+ if ($a === 404) {
+ $a = 403;
+ }
+}
+
+class Whatever
+{
+ protected $_protectedArray = array(
+ 'normalString' => 'That email address is already in use!',
+ 'offendingString' => <<<'STRING'
+Each line of this string is always said to be at column 0,
+ no matter how many spaces are placed
+ at the beginning of each line
+and the ending STRING on the next line is reported as having to be indented.
+STRING
+ );
+}
+
+class MyClass
+{
+ public static function myFunction()
+ {
+ if (empty($keywords) === FALSE) {
+ $keywords = 'foo';
+ $existing = 'foo';
+ }
+
+ return $keywords;
+
+ }//end myFunction()
+
+}//end class
+
+$var = call_user_func(
+ $new_var = function () use (&$a) {
+ if ($a > 0) {
+ return $a++;
+ } else {
+ return $a--;
+ }
+ }
+);
+
+class AnonymousFn
+{
+ public function getAnonFn()
+ {
+ return array(
+ 'functions' => Array(
+ 'function1' => function ($a, $b, $c) {
+ $a = $b + $c;
+ $b = $c / 2;
+ return Array($a, $b, $c);
+ },
+ ),
+ );
+ }
+}
+?>
+
+<div>
+ <?php
+ if ($myvar == 'test') {
+ echo 'something';
+ }
+ ?>
+<div>
+<div>
+ <?php
+ if ($myvar == 'test') {
+ echo 'something';
+ }
+ ?>
+<div>
+<div>
+ <?php
+ if ($myvar == 'test') {
+ echo 'something';
+ }
+ ?>
+<div>
+<body>
+ <?php
+ if (isset($_GET["test"])) {
+ if ($_GET["test"] <> "") {
+ $test = true;
+ } else {
+ $test = true;
+ }
+ }
+ ?>
+</body>
+
+<?php
+if (true) {
+ ?>
+ <div>
+ <div>
+ <div>
+ <?php
+ if (true) {
+ echo 'hello';
+ } else {
+ echo 'goodbye';
+ }
+ ?>
+ </div>
+ </div>
+ </div>
+ <?php
+} else {
+ echo 'else';
+}
+?>
+<?php if (isset($param)) { ?>
+ <h3>some text</h3>
+<?php }
+
+$list = [
+ 'fn' => function ($a) {
+ if ($a === true) {
+ echo 'hi';
+ }
+ }
+];
+
+$list = [
+ 'fn' => function ($a) {
+ if ($a === true) {
+ echo 'hi';
+ }
+ }
+];
+
+if ($foo) {
+ foreach ($bar as $baz) {
+ if ($baz) {
+ ?>
+ <div>
+ <div>
+ <div>
+ <?php
+ if ($baz > 1) {
+ echo '1';
+ }
+ ?>
+ </div>
+ <?php
+ if ($baz > 1) {
+ echo '1';
+ }
+ ?>
+ </div>
+ <?php
+ if ($baz > 1) {
+ echo '1';
+ }
+ ?>
+ </div>
+ <?php
+ }
+ }
+}
+
+?>
+<title><?= CHtml::encode($this->pageTitle); ?></title>
+
+<?php
+if ($foo) {
+ echo '1';
+ echo '2';
+ echo '3';
+}
+
+$array = array();
+array_map(
+ function($x)
+ {
+ return trim($x);
+ },
+ $array
+);
+
+function foo(){return function(){};}
+
+$mockedDatabase->expects($this->at(2))
+ ->with($this->callback(
+ function ($subject)
+ {
+ }
+ )
+ );
+
+/** @var Database $mockedDatabase */
+/** @var Container $mockedContainer */
+
+echo $string->append('foo')
+ ->appaend('bar')
+ ->appaend('baz')
+ ->outputUsing(
+ function ()
+ {
+ }
+ );
+
+echo PHP_EOL;
+
+switch ($arg) {
+ case 1:
+ break;
+ case 2:
+ if ($arg2 == 'foo') {
+ }
+ case 3:
+ default:
+ echo 'default';
+}
+
+if ($tokens[$stackPtr]['content']{0} === '#') {
+} else if ($tokens[$stackPtr]['content']{0} === '/'
+ && $tokens[$stackPtr]['content']{1} === '/'
+) {
+}
+
+$var = call_user_func(
+ function() {
+ if ($foo) {
+ $new_var = function() {
+ if ($a > 0) {
+ return $a++;
+ } else {
+ return $a--;
+ }
+ }
+ }
+ }
+);
+
+a(
+ function() {
+ $a = function() {
+ $b = false;
+ };
+ true
+ }
+);
+
+$var = [
+ [
+ '1' =>
+ function () {
+ return true;
+ },
+ ],
+ [
+ '1' =>
+ function () {
+ return true;
+ },
+ '2' => true,
+ ]
+];
+
+if ($foo) {
+ ?>
+ </p>
+ <?php
+}
+
+function foo()
+{
+ $failingTests[$testName][] = array(
+ 'comment' => self::_replaceKeywords($failingComment, $result),
+ 'screenshot' => Test::getScreenshotPath(
+ $projectid,
+ $result['class_name'],
+ ),
+ );
+
+}
+
+$this->mockedDatabase
+ ->with(
+ $this->callback(
+ function () {
+ return;
+ }
+ )
+ );
+
+$this->subject->recordLogin();
+
+function a()
+{
+ if (true) {
+ static::$a[$b] =
+ static::where($c)
+ ->where($c)
+ ->where(
+ function ($d) {
+ $d->whereNull();
+ $d->orWhere();
+ }
+ )
+ ->first();
+
+ if (static::$a[$b] === null) {
+ static::$a[$b] = new static(
+ array(
+ 'a' => $a->id,
+ 'a' => $a->id,
+ )
+ );
+ }
+ }
+
+ return static::$a[$b];
+}
+
+$foo->load(
+ array(
+ 'bar' => function ($baz) {
+ $baz->call();
+ }
+ )
+);
+
+hello();
+
+$foo = array_unique(
+ array_map(
+ function ($entry) {
+ return $entry * 2;
+ },
+ array()
+ )
+);
+bar($foo);
+
+class PHP_CodeSniffer_Tokenizers_JS
+{
+
+ public $scopeOpeners = array(
+ T_CASE => array(
+ 'end' => array(
+ T_BREAK => T_BREAK,
+ T_RETURN => T_RETURN,
+ ),
+ 'strict' => true,
+ ),
+ );
+}
+
+echo $string->
+ append('foo')->
+ appaend('bar')->
+ appaend('baz')->
+ outputUsing(
+ function ()
+ {
+ }
+ );
+
+$str = 'the items I want to show are: ' .
+ implode(
+ ', ',
+ array('a', 'b', 'c')
+ );
+
+echo $str;
+
+$str = 'foo'
+ . '1'
+ . '2';
+
+echo $str;
+
+bar([
+ 'foo' => foo(function () {
+ return 'foo';
+ })
+]);
+
+$domains = array_unique(
+ array_map(
+ function ($url) {
+ $urlObject = new \Purl\Url($url);
+ return $urlObject->registerableDomain;
+ },
+ $sites
+ )
+);
+
+return $domains;
+
+if ($a == 5) :
+ echo "a equals 5";
+ echo "...";
+elseif ($a == 6) :
+ echo "a equals 6";
+ echo "!!!";
+else :
+ echo "a is neither 5 nor 6";
+endif;
+
+if ($foo):
+ if ($bar) $foo = 1;
+ elseif ($baz) $foo = 2;
+endif;
+
+$this
+ ->method(array(
+ 'foo' => 'bar',
+ ), 'arg', array(
+ 'foo' => 'bar',
+ ));
+
+class Foo
+{
+ use Bar {
+ myMethod as renamedMethod;
+ }
+}
+
+class Foo
+{
+ use Bar {
+ myMethod as renamedMethod;
+ }
+}
+
+foo();
+
+array(
+ 'key1' => function ($bar) {
+ return $bar;
+ },
+ 'key2' => function ($foo) {
+ return $foo;
+ },
+);
+
+?>
+<script>
+ var foo = <?php echo bar(
+ 'baz'
+ ); ?>;
+</script>
+<?php
+
+try {
+ echo 'foo';
+} catch (\Exception $e) {
+ echo 'catch';
+} finally {
+ if (false) {
+ echo 'finally false';
+ }
+}
+
+class C0
+{
+ public function m0()
+ {
+ }
+
+ public function m1()
+ {
+ }
+}
+
+namespace Foo {
+
+ use \Foo\Bar;
+ use \Foo\Baz;
+
+ function test() {
+ return true;
+ }
+
+}
+
+declare(ticks=1) {
+ echo 'foo';
+}
+
+$a =
+ (
+ $b = 1
+ );
+$c = 2;
+
+$a =
+ [
+ $b => 1,
+ ];
+$c = 2;
+
+class foo
+{
+ public function get()
+ {
+ $foo = ['b' => 'c',
+ 'd' => [
+ ['e' => 'f']
+ ]];
+ echo '42';
+
+ $foo = array('b' => 'c',
+ 'd' => array(
+ array('e' => 'f')
+ ));
+ echo '42';
+ }
+}
+
+switch ($foo) {
+ case 1:
+ return array();
+ case 2:
+ return '';
+ case 3:
+ return $function();
+ case 4:
+ return $functionCall($param[0]);
+ case 5:
+ return array() + array(); // Array Merge
+ case 6:
+ // String connect
+ return $functionReturningString('') . $functionReturningString(array());
+ case 7:
+ return functionCall(
+ $withMultiLineParam[0],
+ array(),
+ $functionReturningString(
+ $withMultiLineParam[1]
+ )
+ );
+ case 8:
+ return $param[0][0];
+}
+
+class Test {
+
+ public
+ $foo
+ ,$bar
+ ,$baz = [ ]
+ ;
+
+ public function wtfindent() {
+ }
+}
+
+switch ($x) {
+ case 1:
+ return [1];
+ default:
+ return [2];
+}
+
+switch ($foo) {
+ case self::FOO:
+ return $this->bar($gfoo, function ($id) {
+ return FOO::bar($id);
+ }, $values);
+ case self::BAR:
+ $values = $this->bar($foo, $values);
+ break;
+}
+
+$var = array(
+ 'long description' =>
+ array(0, 'something'),
+ 'another long description' =>
+ array(1, "something else")
+);
+
+$services = array(
+ 'service 1' =>
+ Mockery::mock('class 1')
+ ->shouldReceive('setFilter')->once()
+ ->shouldReceive('getNbResults')->atLeast()->once()
+ ->shouldReceive('getSlice')->once()->andReturn(array())
+ ->getMock(),
+ 'service 2' =>
+ Mockery::mock('class 2')
+ ->shouldReceive('__invoke')->once()
+ ->getMock()
+);
+
+class Foo
+{
+ public function setUp()
+ {
+ $this->foo = new class {
+ public $name = 'Some value';
+ };
+ }
+}
+
+try {
+ foo();
+} catch (\Exception $e) {
+ $foo = function() {
+ return 'foo';
+ }
+
+ if (true) {
+ }
+}
+
+if ($foo) {
+ foo();
+} else if (\Exception $e) {
+ $foo = function() {
+ return 'foo';
+ }
+
+ if (true) {
+ }
+} else {
+ $foo = function() {
+ return 'foo';
+ }
+
+ if (true) {
+ }
+}
+
+switch ($parameter) {
+ case null:
+ return [
+ 'foo' => in_array(
+ 'foo',
+ []
+ ),
+ ];
+
+ default:
+ return [];
+}
+
+class SomeClass
+{
+ public function someFunc()
+ {
+ a(function () {
+ echo "a";
+ })->b(function () {
+ echo "b";
+ });
+
+ if (true) {
+ echo "c";
+ }
+ echo "d";
+ }
+}
+
+$params = self::validate_parameters(self::read_competency_framework_parameters(),
+ array(
+ 'id' => $id,
+ ));
+
+$framework = api::read_framework($params['id']);
+self::validate_context($framework->get_context());
+$output = $PAGE->get_renderer('tool_lp');
+
+class Test123
+{
+ protected static
+ $prop1 = [
+ 'testA' => 123,
+ ],
+ $prop2 = [
+ 'testB' => 456,
+ ],
+
+ protected static
+ $prop3 = array(
+ 'testA' => 123,
+ ),
+ $prop4 = array(
+ 'testB' => 456,
+ ),
+
+ protected static $prop5;
+}
+
+$foo = foo(
+ function () {
+ $foo->debug(
+ $a,
+ $b
+ );
+
+ if ($a) {
+ $b = $a;
+ }
+ }
+);
+
+if (somethingIsTrue()) {
+ ?>
+ <div>
+ <?php
+ $someVar = doSomething(
+ 'foobar',
+ true,
+ 'foo',
+ 'bar'
+ );
+
+ if (somethingElseIsTrue()) {
+ doOneThing();
+ } else {
+ doTheOtherThing();
+ }
+ ?>
+ </div>
+ <?php
+}
+
+$foo = [
+ new class() implements Bar {
+
+ public static function foo(): string
+ {
+ return 'foo';
+ }
+ },
+];
+
+$foo = [
+ function () {
+ if ($foo) {
+ return 'foo';
+ }
+ },
+];
+
+$foo
+ ->bar(foo(function () {
+ }), foo(function () {
+ }));
+
+echo 'foo';
+
+class Test {
+
+ public function a() {
+ ?>a<?php
+ }
+
+ public function b(
+ $a
+ ) {
+ echo $a;
+ }
+}
+
+$foo = foo(
+ function () {
+ $foo->debug(
+ $a,
+ $b
+ );
+
+ if ($a) {
+ $b = $a;
+ }
+ }
+);
+
+function test()
+{
+ $array = [];
+ foreach ($array as $data) {
+ [
+ 'key1' => $var1,
+ 'key2' => $var2,
+ ] = $data;
+ foreach ($var1 as $method) {
+ echo $method . $var2;
+ }
+ }
+}
+
+switch ($a) {
+ case 0:
+ function () {
+ };
+ case 1:
+ break;
+}
+
+class Test
+{
+ public function __construct()
+ {
+ if (false) {
+ echo 0;
+ }
+ }
+}
+
+return [
+ 'veryLongKeySoIWantToMakeALineBreak'
+ => 'veryLonValueSoIWantToMakeALineBreak',
+
+ 'someOtherKey' => [
+ 'someValue'
+ ],
+
+ 'arrayWithArraysInThere' => [
+ ['Value1', 'Value1']
+ ],
+];
+
+switch ($sContext) {
+ case 'SOMETHING':
+ case 'CONSTANT':
+ do_something();
+ break;
+ case 'GLOBAL':
+ case 'GLOBAL1':
+ do_something();
+ // Fall through
+ default:
+ {
+ do_something();
+ }
+}
+
+public function foo()
+{
+ $foo('some
+ long description', function () {
+ });
+
+ $foo('some
+ long
+ description', function () {
+ });
+
+ $foo(
+ 'some long description', function () {
+ });
+}
+
+function foo()
+{
+ $foo = array(
+ );
+
+ if ($foo) {
+ echo 'foo';
+ }
+
+ return false;
+}
+
+?>
+<?php
+if (true) {
+}
+else if (true) {
+}
+
+$a = 1;
+
+/*
+$a = array(
+ */
+);
+
+echo ""
--- /dev/null
+@codingStandardsChangeSetting Generic.WhiteSpace.ScopeIndent tabIndent false
+var script = document.createElement('script');
+script.onload = function()
+{
+ clearTimeout(t);
+ script456.onload = null;
+ script.onreadystatechange = null;
+ callback.call(this);
+
+};
+
+this.callbacks[type] = {
+ namespaces: {},
+others: []
+};
+
+blah = function()
+{
+ print something;
+
+ }
+
+test(blah, function() {
+ print something;
+});
+
+var test = [{x: 10}];
+var test = [{
+ x: 10,
+ y: {
+ b14h: 12,
+ 'b14h': 12
+ },
+ z: 23
+}];
+
+Viper.prototype = {
+
+ _removeEvents: function(elem)
+ {
+ if (!elem) {
+ elem = this.element;
+ }
+
+ ViperUtil.removeEvent(elem, '.' + this.getEventNamespace());
+
+ }
+
+};
+
+this.init = function(data) {
+ if (_pageListWdgt) {
+ GUI.getWidget('changedPagesList').addItemClickedCallback(
+ function(itemid, target) {
+ draftChangeTypeClicked(
+ itemid,
+ target,
+ {
+ reviewData: _reviewData,
+ pageid: itemid
+ }
+ );
+ }
+ );
+ }//end if
+
+};
+
+a(
+ function() {
+ var _a = function() {
+ b = false;
+
+ };
+ true
+ }
+);
+
+(function() {
+ a = function() {
+ a(function() {
+ if (true) {
+ a = true;
+ }
+ });
+
+ a(
+ function() {
+ if (true) {
+ if (true) {
+ a = true;
+ }
+ }
+ }
+ );
+
+ a(
+ function() {
+ if (true) {
+ a = true;
+ }
+ }
+ );
+
+ };
+
+})();
+
+a.prototype = {
+
+ a: function()
+ {
+ var currentSize = null;
+ ViperUtil.addEvent(
+ header,
+ 'safedblclick',
+ function() {},
+ );
+
+ if (topContent) {
+ ViperUtil.addClass(topContent, 'Viper-popup-top');
+ main.appendChild(topContent);
+ }
+
+ ViperUtil.addClass(midContent, 'Viper-popup-content');
+ main.appendChild(midContent);
+ }
+
+};
+
+a.prototype = {
+
+ a: function()
+ {
+ ViperUtil.addClass(midContent, 'Viper-popup-content');
+ main.appendChild(midContent);
+
+ var mouseUpAction = function() {};
+ var preventMouseUp = false;
+ var self = this;
+ if (clickAction) {
+ }
+ }
+
+};
+
+a.prototype = {
+
+ a: function()
+ {
+ var a = function() {
+ var a = 'foo';
+ };
+
+ if (true) {
+ }
+
+ },
+
+ b: function()
+ {
+ ViperUtil.addEvent(
+ function() {
+ if (fullScreen !== true) {
+ currentSize = {
+ };
+
+ showfullScreen();
+ }
+ }
+ );
+
+ },
+
+ c: function()
+ {
+ this.a(
+ {
+ a: function() {
+ form.onsubmit = function() {
+ return false;
+ };
+
+ var a = true;
+ }
+ }
+ );
+
+ }
+
+};
+
+a.prototype = {
+ init: function()
+ {},
+
+ _b: function()
+ {
+ }
+
+};
+
+for (var i = 0; i < 10; i++) {
+ var foo = {foo:{'a':'b',
+ 'c':'d'}};
+}
+
+class TestOk
+{
+ destroy()
+ {
+ setTimeout(a, 1000);
+
+ if (typeof self.callbackOnClose === "function") {
+ self.callbackOnClose();
+ }
+ }
+}
+
+class TestBad
+{
+ destroy()
+ {
+ setTimeout(function () {
+ return;
+ }, 1000);
+
+ if (typeof self.callbackOnClose === "function") {
+ self.callbackOnClose();
+ }
+ }
+}
+
+( function( $ ) {
+ foo(function( value ) {
+ value.bind( function( newval ) {
+ $( '#bar' ).html( newval );
+ } );
+ } )( jQuery );
--- /dev/null
+@codingStandardsChangeSetting Generic.WhiteSpace.ScopeIndent tabIndent false
+var script = document.createElement('script');
+script.onload = function()
+{
+ clearTimeout(t);
+ script456.onload = null;
+ script.onreadystatechange = null;
+ callback.call(this);
+
+};
+
+this.callbacks[type] = {
+ namespaces: {},
+ others: []
+};
+
+blah = function()
+{
+ print something;
+
+}
+
+test(blah, function() {
+ print something;
+});
+
+var test = [{x: 10}];
+var test = [{
+ x: 10,
+ y: {
+ b14h: 12,
+ 'b14h': 12
+ },
+ z: 23
+}];
+
+Viper.prototype = {
+
+ _removeEvents: function(elem)
+ {
+ if (!elem) {
+ elem = this.element;
+ }
+
+ ViperUtil.removeEvent(elem, '.' + this.getEventNamespace());
+
+ }
+
+};
+
+this.init = function(data) {
+ if (_pageListWdgt) {
+ GUI.getWidget('changedPagesList').addItemClickedCallback(
+ function(itemid, target) {
+ draftChangeTypeClicked(
+ itemid,
+ target,
+ {
+ reviewData: _reviewData,
+ pageid: itemid
+ }
+ );
+ }
+ );
+ }//end if
+
+};
+
+a(
+ function() {
+ var _a = function() {
+ b = false;
+
+ };
+ true
+ }
+);
+
+(function() {
+ a = function() {
+ a(function() {
+ if (true) {
+ a = true;
+ }
+ });
+
+ a(
+ function() {
+ if (true) {
+ if (true) {
+ a = true;
+ }
+ }
+ }
+ );
+
+ a(
+ function() {
+ if (true) {
+ a = true;
+ }
+ }
+ );
+
+ };
+
+})();
+
+a.prototype = {
+
+ a: function()
+ {
+ var currentSize = null;
+ ViperUtil.addEvent(
+ header,
+ 'safedblclick',
+ function() {},
+ );
+
+ if (topContent) {
+ ViperUtil.addClass(topContent, 'Viper-popup-top');
+ main.appendChild(topContent);
+ }
+
+ ViperUtil.addClass(midContent, 'Viper-popup-content');
+ main.appendChild(midContent);
+ }
+
+};
+
+a.prototype = {
+
+ a: function()
+ {
+ ViperUtil.addClass(midContent, 'Viper-popup-content');
+ main.appendChild(midContent);
+
+ var mouseUpAction = function() {};
+ var preventMouseUp = false;
+ var self = this;
+ if (clickAction) {
+ }
+ }
+
+};
+
+a.prototype = {
+
+ a: function()
+ {
+ var a = function() {
+ var a = 'foo';
+ };
+
+ if (true) {
+ }
+
+ },
+
+ b: function()
+ {
+ ViperUtil.addEvent(
+ function() {
+ if (fullScreen !== true) {
+ currentSize = {
+ };
+
+ showfullScreen();
+ }
+ }
+ );
+
+ },
+
+ c: function()
+ {
+ this.a(
+ {
+ a: function() {
+ form.onsubmit = function() {
+ return false;
+ };
+
+ var a = true;
+ }
+ }
+ );
+
+ }
+
+};
+
+a.prototype = {
+ init: function()
+ {},
+
+ _b: function()
+ {
+ }
+
+};
+
+for (var i = 0; i < 10; i++) {
+ var foo = {foo:{'a':'b',
+ 'c':'d'}};
+}
+
+class TestOk
+{
+ destroy()
+ {
+ setTimeout(a, 1000);
+
+ if (typeof self.callbackOnClose === "function") {
+ self.callbackOnClose();
+ }
+ }
+}
+
+class TestBad
+{
+ destroy()
+ {
+ setTimeout(function () {
+ return;
+ }, 1000);
+
+ if (typeof self.callbackOnClose === "function") {
+ self.callbackOnClose();
+ }
+ }
+}
+
+( function( $ ) {
+ foo(function( value ) {
+ value.bind( function( newval ) {
+ $( '#bar' ).html( newval );
+ } );
+ } )( jQuery );
--- /dev/null
+@codingStandardsChangeSetting Generic.WhiteSpace.ScopeIndent tabIndent true
+<meta name="twitter:site" content="<?php echo $metaTagsData['twitter:site']; ?>">
+<?php
+class Test {
+ function __construct()
+ {
+ $this->hello();
+ }
+
+ function hello()
+ {
+ echo 'hello';
+}//end hello()
+
+ function hello2()
+ {
+ if (TRUE) {
+ echo 'hello'; // no error here as its more than 4 spaces.
+ } else {
+ echo 'bye';
+ }
+
+ while (TRUE) {
+ echo 'hello';
+ }
+
+ do {
+ echo 'hello';
+ } while (TRUE);
+ }
+
+ function hello3()
+ {
+ switch ($hello) {
+ case 'hello':
+ break;
+ }
+ }
+
+}
+
+?>
+<pre>
+</head>
+<body>
+<?php
+if ($form->validate()) {
+ $safe = $form->getSubmitValues();
+}
+?>
+</pre>
+<?php
+
+class Test2
+{
+ function __construct()
+ {
+ // $this->open(); // error here
+ }
+
+ public function open()
+ {
+ // Some inline stuff that shouldn't error
+ if (TRUE) echo 'hello';
+ foreach ($tokens as $token) echo $token;
+ }
+
+ /**
+ * This is a comment 1.
+ * This is a comment 2.
+ * This is a comment 3.
+ * This is a comment 4.
+ */
+ public function close()
+ {
+ // All ok.
+ if (TRUE) {
+ if (TRUE) {
+ } else if (FALSE) {
+ foreach ($tokens as $token) {
+ switch ($token) {
+ case '1':
+ case '2':
+ if (true) {
+ if (false) {
+ if (false) {
+ if (false) {
+ echo 'hello';
+ }
+ }
+ }
+ }
+ break;
+ case '5':
+ break;
+ }
+ do {
+ while (true) {
+ foreach ($tokens as $token) {
+ for ($i = 0; $i < $token; $i++) {
+ echo 'hello';
+ }
+ }
+ }
+ } while (true);
+ }
+ }
+ }
+ }
+
+ /*
+ This is another c style comment 1.
+ This is another c style comment 2.
+ This is another c style comment 3.
+ This is another c style comment 4.
+ This is another c style comment 5.
+ */
+
+ /* This is a T_COMMENT
+ *
+ *
+ *
+ */
+
+ /** This is a T_DOC_COMMENT
+ */
+
+ /*
+ This T_COMMENT has a newline in it.
+
+ */
+
+ public function read()
+ {
+ echo 'hello';
+
+ // no errors below.
+ $array = array(
+ 'this',
+ 'that' => array(
+ 'hello',
+ 'hello again' => array(
+ 'hello',
+ ),
+ ),
+ );
+ }
+}
+
+abstract class Test3
+{
+ public function parse()
+ {
+
+ foreach ($t as $ndx => $token) {
+ if (is_array($token)) {
+ echo 'here';
+ } else {
+ $ts[] = array("token" => $token, "value" => '');
+
+ $last = count($ts) - 1;
+
+ switch ($token) {
+ case '(':
+
+ if ($last >= 3 &&
+ $ts[0]['token'] != T_CLASS &&
+ $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
+ $ts[$last - 3]['token'] == T_VARIABLE ) {
+
+
+ if (true) {
+ echo 'hello';
+ }
+ }
+ array_push($braces, $token);
+ break;
+ }
+ }
+ }
+ }
+}
+
+public function test()
+{
+ $o = <<<EOF
+this is some text
+this is some text
+this is some text
+this is some text
+this is some text
+this is some text
+EOF;
+
+ return $o;
+}
+
+if ($a === true || $a === true || $a === true || $a === true ||
+ $a === true || $a === true || $a === true || $a === true) {
+
+ echo 'hello';
+}
+
+if ($true) {
+ /* First comment line
+ *
+ * Comment test here
+ * Comment test here
+ *
+ */
+
+ /* First comment line
+ *
+ * Comment test here
+ * Comment test here
+ *
+ this si something */
+}
+
+function test()
+{
+ /* taken from http://de3.php.net/manual/en/reserved.php */
+ # $m[] = 'declare';
+ /* taken from http://de3.php.net/manual/en/reserved.php */
+ # $m[] = 'declare';
+}
+
+foreach ($elements as $element) {
+ if ($something) {
+ // Do IF.
+ } else if ($somethingElse) {
+ // Do ELSE.
+ }
+}
+
+if ($condition) {
+ echo "This is a long
+string that spans $numLines lines
+without indenting.
+";
+}
+
+if ($condition) {
+ echo 'This is a long
+ string that spans multiple lines
+ with indenting.
+ ';
+}
+
+if ($condition) {
+ echo 'This is a long
+ string that spans multiple lines
+ with indenting.';
+}
+
+switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ echo $string{1};
+ }
+ break;
+}
+
+function temp($foo, $bar) {
+ switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ return $foo;
+ }
+ break;
+ }
+}
+
+switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ if ($something) {
+ echo $string{1};
+ } else if ($else) {
+ switch ($else) {
+ default:
+ }
+ }
+ }
+ break;
+}
+
+switch ($name) {
+ case "1":
+ case "2":
+ case "3":
+ return true;
+}
+
+switch ($name) {
+ case "1":
+ case "2":
+ case "3":
+ default :
+ return true;
+}
+
+function myFunction()
+{
+ ?>
+ <dynamic_content>
+
+ </dynamic_content>
+ <?php
+
+}
+
+switch ($name) {
+ case "1":
+ switch ($name2) {
+ case "1":
+ break;
+ case "2":
+ break;
+ }
+ break;
+ case "2":
+ break;
+}
+
+switch (true) {
+ case true: {
+ }
+ echo 'hi';
+ break;
+ case false:
+ case null:{
+ echo 'hi';
+ }
+ break;
+}
+
+switch (true) {
+ case true: {
+ echo 'hi';
+ }
+ // No break here.
+ case false:
+ case true:
+ case null:{
+ echo 'hi';
+ echo 'hi';
+ }
+ break;
+}
+
+switch (true) {
+ case true: {
+ if (true) {
+ echo 'hi';
+ }
+ }
+ break;
+}
+
+// Testing anon function.
+class a {
+ function c()
+ {
+ $this->doSomething(
+ function () {
+ echo 123;
+ }
+ );
+ }
+}
+
+some_function(
+ function() {
+ $a = 403;
+ if ($a === 404) {
+ $a = 403;
+ }
+ }
+);
+
+some_function(
+ function() {
+ $a = 403;
+ if ($a === 404) {
+ $a = 403;
+ }
+ }
+);
+
+$myFunction = function() {
+ $a = 403;
+ if ($a === 404) {
+ $a = 403;
+ }
+}
+
+class Whatever
+{
+ protected $_protectedArray = array(
+ 'normalString' => 'That email address is already in use!',
+ 'offendingString' => <<<'STRING'
+Each line of this string is always said to be at column 0,
+ no matter how many spaces are placed
+ at the beginning of each line
+and the ending STRING on the next line is reported as having to be indented.
+STRING
+ );
+}
+
+class MyClass
+{
+ public static function myFunction()
+ {
+ if (empty($keywords) === FALSE) {
+ $keywords = 'foo';
+ $existing = 'foo';
+ }
+
+ return $keywords;
+
+ }//end myFunction()
+
+}//end class
+
+$var = call_user_func(
+ $new_var = function () use (&$a) {
+ if ($a > 0) {
+ return $a++;
+ } else {
+ return $a--;
+ }
+ }
+);
+
+class AnonymousFn
+{
+ public function getAnonFn()
+ {
+ return array(
+ 'functions' => Array(
+ 'function1' => function ($a, $b, $c) {
+ $a = $b + $c;
+ $b = $c / 2;
+ return Array($a, $b, $c);
+ },
+ ),
+ );
+ }
+}
+?>
+
+<div>
+ <?php
+ if ($myvar == 'test') {
+ echo 'something';
+ }
+ ?>
+<div>
+<div>
+ <?php
+ if ($myvar == 'test') {
+ echo 'something';
+ }
+ ?>
+<div>
+<div>
+ <?php
+if ($myvar == 'test') {
+ echo 'something';
+}
+ ?>
+<div>
+<body>
+ <?php
+ if (isset($_GET["test"])) {
+ if ($_GET["test"] <> "") {
+ $test = true;
+ } else {
+ $test = true;
+ }
+ }
+ ?>
+</body>
+
+<?php
+if (true) {
+ ?>
+ <div>
+ <div>
+ <div>
+ <?php
+ if (true) {
+ echo 'hello';
+ } else {
+ echo 'goodbye';
+ }
+ ?>
+ </div>
+ </div>
+ </div>
+ <?php
+} else {
+ echo 'else';
+}
+?>
+<?php if (isset($param)) { ?>
+ <h3>some text</h3>
+<?php }
+
+$list = [
+ 'fn' => function ($a) {
+ if ($a === true) {
+ echo 'hi';
+ }
+ }
+];
+
+$list = [
+ 'fn' => function ($a) {
+ if ($a === true) {
+ echo 'hi';
+ }
+ }
+];
+
+if ($foo) {
+ foreach ($bar as $baz) {
+ if ($baz) {
+ ?>
+ <div>
+ <div>
+ <div>
+ <?php
+ if ($baz > 1) {
+ echo '1';
+ }
+ ?>
+ </div>
+ <?php
+ if ($baz > 1) {
+ echo '1';
+ }
+ ?>
+ </div>
+ <?php
+ if ($baz > 1) {
+ echo '1';
+ }
+ ?>
+ </div>
+ <?php
+ }
+ }
+}
+
+?>
+<title><?= CHtml::encode($this->pageTitle); ?></title>
+
+<?php
+if ($foo) {
+ echo '1';
+ echo '2';
+ echo '3';
+}
+
+$array = array();
+array_map(
+ function($x)
+ {
+ return trim($x);
+ },
+ $array
+);
+
+function foo(){return function(){};}
+
+$mockedDatabase->expects($this->at(2))
+ ->with($this->callback(
+ function ($subject)
+ {
+ }
+ )
+ );
+
+/** @var Database $mockedDatabase */
+/** @var Container $mockedContainer */
+
+echo $string->append('foo')
+ ->appaend('bar')
+ ->appaend('baz')
+ ->outputUsing(
+ function ()
+ {
+ }
+ );
+
+echo PHP_EOL;
+
+switch ($arg) {
+ case 1:
+ break;
+ case 2:
+ if ($arg2 == 'foo') {
+ }
+ case 3:
+ default:
+ echo 'default';
+}
+
+if ($tokens[$stackPtr]['content']{0} === '#') {
+} else if ($tokens[$stackPtr]['content']{0} === '/'
+ && $tokens[$stackPtr]['content']{1} === '/'
+) {
+}
+
+$var = call_user_func(
+ function() {
+ if ($foo) {
+ $new_var = function() {
+ if ($a > 0) {
+ return $a++;
+ } else {
+ return $a--;
+ }
+ }
+ }
+ }
+);
+
+a(
+ function() {
+ $a = function() {
+ $b = false;
+ };
+ true
+ }
+);
+
+$var = [
+ [
+ '1' =>
+ function () {
+ return true;
+ },
+ ],
+ [
+ '1' =>
+ function () {
+ return true;
+ },
+ '2' => true,
+ ]
+];
+
+if ($foo) {
+ ?>
+ </p>
+ <?php
+}
+
+function foo()
+{
+ $failingTests[$testName][] = array(
+ 'comment' => self::_replaceKeywords($failingComment, $result),
+ 'screenshot' => Test::getScreenshotPath(
+ $projectid,
+ $result['class_name'],
+ ),
+ );
+
+}
+
+$this->mockedDatabase
+ ->with(
+ $this->callback(
+ function () {
+ return;
+ }
+ )
+ );
+
+$this->subject->recordLogin();
+
+function a()
+{
+ if (true) {
+ static::$a[$b] =
+ static::where($c)
+ ->where($c)
+ ->where(
+ function ($d) {
+ $d->whereNull();
+ $d->orWhere();
+ }
+ )
+ ->first();
+
+ if (static::$a[$b] === null) {
+ static::$a[$b] = new static(
+ array(
+ 'a' => $a->id,
+ 'a' => $a->id,
+ )
+ );
+ }
+ }
+
+ return static::$a[$b];
+}
+
+$foo->load(
+ array(
+ 'bar' => function ($baz) {
+ $baz->call();
+ }
+ )
+);
+
+hello();
+
+$foo = array_unique(
+ array_map(
+ function ($entry) {
+ return $entry * 2;
+ },
+ array()
+ )
+);
+bar($foo);
+
+class PHP_CodeSniffer_Tokenizers_JS
+{
+
+ public $scopeOpeners = array(
+ T_CASE => array(
+ 'end' => array(
+ T_BREAK => T_BREAK,
+ T_RETURN => T_RETURN,
+ ),
+ 'strict' => true,
+ ),
+ );
+}
+
+echo $string->
+ append('foo')->
+ appaend('bar')->
+ appaend('baz')->
+ outputUsing(
+ function ()
+ {
+ }
+ );
+
+$str = 'the items I want to show are: ' .
+ implode(
+ ', ',
+ array('a', 'b', 'c')
+ );
+
+echo $str;
+
+$str = 'foo'
+ . '1'
+ . '2';
+
+echo $str;
+
+bar([
+ 'foo' => foo(function () {
+ return 'foo';
+ })
+]);
+
+$domains = array_unique(
+ array_map(
+ function ($url) {
+ $urlObject = new \Purl\Url($url);
+ return $urlObject->registerableDomain;
+ },
+ $sites
+ )
+);
+
+return $domains;
+
+if ($a == 5) :
+ echo "a equals 5";
+ echo "...";
+elseif ($a == 6) :
+ echo "a equals 6";
+ echo "!!!";
+else :
+ echo "a is neither 5 nor 6";
+endif;
+
+if ($foo):
+if ($bar) $foo = 1;
+elseif ($baz) $foo = 2;
+endif;
+
+$this
+ ->method(array(
+ 'foo' => 'bar',
+ ), 'arg', array(
+ 'foo' => 'bar',
+ ));
+
+class Foo
+{
+ use Bar {
+ myMethod as renamedMethod;
+ }
+}
+
+class Foo
+{
+ use Bar {
+ myMethod as renamedMethod;
+ }
+}
+
+foo();
+
+array(
+ 'key1' => function ($bar) {
+ return $bar;
+ },
+ 'key2' => function ($foo) {
+ return $foo;
+ },
+);
+
+?>
+<script>
+ var foo = <?php echo bar(
+ 'baz'
+ ); ?>;
+</script>
+<?php
+
+try {
+ echo 'foo';
+} catch (\Exception $e) {
+ echo 'catch';
+} finally {
+ if (false) {
+ echo 'finally false';
+ }
+}
+
+class C0
+{
+ public function m0()
+ {
+ }
+
+ public function m1()
+ {
+ }
+}
+
+namespace Foo {
+
+ use \Foo\Bar;
+ use \Foo\Baz;
+
+ function test() {
+ return true;
+ }
+
+}
+
+declare(ticks=1) {
+echo 'foo';
+}
+
+$a =
+ (
+ $b = 1
+ );
+$c = 2;
+
+$a =
+ [
+ $b => 1,
+ ];
+$c = 2;
+
+class foo
+{
+ public function get()
+ {
+ $foo = ['b' => 'c',
+ 'd' => [
+ ['e' => 'f']
+ ]];
+ echo '42';
+
+ $foo = array('b' => 'c',
+ 'd' => array(
+ array('e' => 'f')
+ ));
+ echo '42';
+ }
+}
+
+switch ($foo) {
+ case 1:
+ return array();
+ case 2:
+ return '';
+ case 3:
+ return $function();
+ case 4:
+ return $functionCall($param[0]);
+ case 5:
+ return array() + array(); // Array Merge
+ case 6:
+ // String connect
+ return $functionReturningString('') . $functionReturningString(array());
+ case 7:
+ return functionCall(
+ $withMultiLineParam[0],
+ array(),
+ $functionReturningString(
+ $withMultiLineParam[1]
+ )
+ );
+ case 8:
+ return $param[0][0];
+}
+
+class Test {
+
+ public
+ $foo
+ ,$bar
+ ,$baz = [ ]
+ ;
+
+ public function wtfindent() {
+ }
+}
+
+switch ($x) {
+ case 1:
+ return [1];
+ default:
+ return [2];
+}
+
+switch ($foo) {
+ case self::FOO:
+ return $this->bar($gfoo, function ($id) {
+ return FOO::bar($id);
+ }, $values);
+ case self::BAR:
+ $values = $this->bar($foo, $values);
+ break;
+}
+
+$var = array(
+ 'long description' =>
+ array(0, 'something'),
+ 'another long description' =>
+ array(1, "something else")
+);
+
+$services = array(
+ 'service 1' =>
+ Mockery::mock('class 1')
+ ->shouldReceive('setFilter')->once()
+ ->shouldReceive('getNbResults')->atLeast()->once()
+ ->shouldReceive('getSlice')->once()->andReturn(array())
+ ->getMock(),
+ 'service 2' =>
+ Mockery::mock('class 2')
+ ->shouldReceive('__invoke')->once()
+ ->getMock()
+);
+
+class Foo
+{
+ public function setUp()
+ {
+ $this->foo = new class {
+ public $name = 'Some value';
+ };
+ }
+}
+
+try {
+ foo();
+} catch (\Exception $e) {
+ $foo = function() {
+ return 'foo';
+ }
+
+ if (true) {
+ }
+}
+
+if ($foo) {
+ foo();
+} else if (\Exception $e) {
+ $foo = function() {
+ return 'foo';
+ }
+
+ if (true) {
+ }
+} else {
+ $foo = function() {
+ return 'foo';
+ }
+
+ if (true) {
+ }
+}
+
+switch ($parameter) {
+ case null:
+ return [
+ 'foo' => in_array(
+ 'foo',
+ []
+ ),
+ ];
+
+ default:
+ return [];
+}
+
+class SomeClass
+{
+ public function someFunc()
+ {
+ a(function () {
+ echo "a";
+ })->b(function () {
+ echo "b";
+ });
+
+ if (true) {
+ echo "c";
+ }
+ echo "d";
+ }
+}
+
+$params = self::validate_parameters(self::read_competency_framework_parameters(),
+ array(
+ 'id' => $id,
+ ));
+
+$framework = api::read_framework($params['id']);
+self::validate_context($framework->get_context());
+$output = $PAGE->get_renderer('tool_lp');
+
+class Test123
+{
+ protected static
+ $prop1 = [
+ 'testA' => 123,
+ ],
+ $prop2 = [
+ 'testB' => 456,
+ ],
+
+ protected static
+ $prop3 = array(
+ 'testA' => 123,
+ ),
+ $prop4 = array(
+ 'testB' => 456,
+ ),
+
+ protected static $prop5;
+}
+
+$foo = foo(
+ function () {
+ $foo->debug(
+ $a,
+ $b
+ );
+
+ if ($a) {
+ $b = $a;
+ }
+ }
+);
+
+if (somethingIsTrue()) {
+ ?>
+ <div>
+ <?php
+ $someVar = doSomething(
+ 'foobar',
+ true,
+ 'foo',
+ 'bar'
+ );
+
+ if (somethingElseIsTrue()) {
+ doOneThing();
+ } else {
+ doTheOtherThing();
+ }
+ ?>
+ </div>
+ <?php
+}
+
+$foo = [
+ new class() implements Bar {
+
+ public static function foo(): string
+ {
+ return 'foo';
+ }
+ },
+];
+
+$foo = [
+ function () {
+ if ($foo) {
+ return 'foo';
+ }
+ },
+];
+
+$foo
+ ->bar(foo(function () {
+ }), foo(function () {
+ }));
+
+echo 'foo';
+
+class Test {
+
+ public function a() {
+ ?>a<?php
+ }
+
+ public function b(
+ $a
+ ) {
+ echo $a;
+ }
+}
+
+$foo = foo(
+ function () {
+ $foo->debug(
+ $a,
+ $b
+ );
+
+ if ($a) {
+ $b = $a;
+ }
+ }
+);
+
+function test()
+{
+ $array = [];
+ foreach ($array as $data) {
+ [
+ 'key1' => $var1,
+ 'key2' => $var2,
+ ] = $data;
+ foreach ($var1 as $method) {
+ echo $method . $var2;
+ }
+ }
+}
+
+switch ($a) {
+ case 0:
+ function () {
+ };
+ case 1:
+ break;
+}
+
+class Test
+{
+ public function __construct()
+ {
+if (false) {
+echo 0;
+ }
+ }
+}
+
+return [
+ 'veryLongKeySoIWantToMakeALineBreak'
+ => 'veryLonValueSoIWantToMakeALineBreak',
+
+ 'someOtherKey' => [
+ 'someValue'
+ ],
+
+ 'arrayWithArraysInThere' => [
+ ['Value1', 'Value1']
+ ],
+];
+
+switch ($sContext) {
+ case 'SOMETHING':
+ case 'CONSTANT':
+ do_something();
+ break;
+ case 'GLOBAL':
+ case 'GLOBAL1':
+ do_something();
+ // Fall through
+ default:
+ {
+ do_something();
+ }
+}
+
+public function foo()
+{
+ $foo('some
+ long description', function () {
+ });
+
+ $foo('some
+ long
+ description', function () {
+ });
+
+ $foo(
+'some long description', function () {
+ });
+}
+
+ function foo()
+ {
+ $foo = array(
+ );
+
+ if ($foo) {
+ echo 'foo';
+ }
+
+ return false;
+ }
+
+?>
+<?php
+ if (true) {
+ }
+ else if (true) {
+ }
+
+$a = 1;
+
+/*
+$a = array(
+ */
+);
+
+echo ""
--- /dev/null
+@codingStandardsChangeSetting Generic.WhiteSpace.ScopeIndent tabIndent true
+<meta name="twitter:site" content="<?php echo $metaTagsData['twitter:site']; ?>">
+<?php
+class Test {
+ function __construct()
+ {
+ $this->hello();
+ }
+
+ function hello()
+ {
+ echo 'hello';
+ }//end hello()
+
+ function hello2()
+ {
+ if (TRUE) {
+ echo 'hello'; // no error here as its more than 4 spaces.
+ } else {
+ echo 'bye';
+ }
+
+ while (TRUE) {
+ echo 'hello';
+ }
+
+ do {
+ echo 'hello';
+ } while (TRUE);
+ }
+
+ function hello3()
+ {
+ switch ($hello) {
+ case 'hello':
+ break;
+ }
+ }
+
+}
+
+?>
+<pre>
+</head>
+<body>
+<?php
+if ($form->validate()) {
+ $safe = $form->getSubmitValues();
+}
+?>
+</pre>
+<?php
+
+class Test2
+{
+ function __construct()
+ {
+ // $this->open(); // error here
+ }
+
+ public function open()
+ {
+ // Some inline stuff that shouldn't error
+ if (TRUE) echo 'hello';
+ foreach ($tokens as $token) echo $token;
+ }
+
+ /**
+ * This is a comment 1.
+ * This is a comment 2.
+ * This is a comment 3.
+ * This is a comment 4.
+ */
+ public function close()
+ {
+ // All ok.
+ if (TRUE) {
+ if (TRUE) {
+ } else if (FALSE) {
+ foreach ($tokens as $token) {
+ switch ($token) {
+ case '1':
+ case '2':
+ if (true) {
+ if (false) {
+ if (false) {
+ if (false) {
+ echo 'hello';
+ }
+ }
+ }
+ }
+ break;
+ case '5':
+ break;
+ }
+ do {
+ while (true) {
+ foreach ($tokens as $token) {
+ for ($i = 0; $i < $token; $i++) {
+ echo 'hello';
+ }
+ }
+ }
+ } while (true);
+ }
+ }
+ }
+ }
+
+ /*
+ This is another c style comment 1.
+ This is another c style comment 2.
+ This is another c style comment 3.
+ This is another c style comment 4.
+ This is another c style comment 5.
+ */
+
+ /* This is a T_COMMENT
+ *
+ *
+ *
+ */
+
+ /** This is a T_DOC_COMMENT
+ */
+
+ /*
+ This T_COMMENT has a newline in it.
+
+ */
+
+ public function read()
+ {
+ echo 'hello';
+
+ // no errors below.
+ $array = array(
+ 'this',
+ 'that' => array(
+ 'hello',
+ 'hello again' => array(
+ 'hello',
+ ),
+ ),
+ );
+ }
+}
+
+abstract class Test3
+{
+ public function parse()
+ {
+
+ foreach ($t as $ndx => $token) {
+ if (is_array($token)) {
+ echo 'here';
+ } else {
+ $ts[] = array("token" => $token, "value" => '');
+
+ $last = count($ts) - 1;
+
+ switch ($token) {
+ case '(':
+
+ if ($last >= 3 &&
+ $ts[0]['token'] != T_CLASS &&
+ $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
+ $ts[$last - 3]['token'] == T_VARIABLE ) {
+
+
+ if (true) {
+ echo 'hello';
+ }
+ }
+ array_push($braces, $token);
+ break;
+ }
+ }
+ }
+ }
+}
+
+public function test()
+{
+ $o = <<<EOF
+this is some text
+this is some text
+this is some text
+this is some text
+this is some text
+this is some text
+EOF;
+
+ return $o;
+}
+
+if ($a === true || $a === true || $a === true || $a === true ||
+ $a === true || $a === true || $a === true || $a === true) {
+
+ echo 'hello';
+}
+
+if ($true) {
+ /* First comment line
+ *
+ * Comment test here
+ * Comment test here
+ *
+ */
+
+ /* First comment line
+ *
+ * Comment test here
+ * Comment test here
+ *
+ this si something */
+}
+
+function test()
+{
+ /* taken from http://de3.php.net/manual/en/reserved.php */
+ # $m[] = 'declare';
+ /* taken from http://de3.php.net/manual/en/reserved.php */
+ # $m[] = 'declare';
+}
+
+foreach ($elements as $element) {
+ if ($something) {
+ // Do IF.
+ } else if ($somethingElse) {
+ // Do ELSE.
+ }
+}
+
+if ($condition) {
+ echo "This is a long
+string that spans $numLines lines
+without indenting.
+";
+}
+
+if ($condition) {
+ echo 'This is a long
+ string that spans multiple lines
+ with indenting.
+ ';
+}
+
+if ($condition) {
+ echo 'This is a long
+ string that spans multiple lines
+ with indenting.';
+}
+
+switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ echo $string{1};
+ }
+ break;
+}
+
+function temp($foo, $bar) {
+ switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ return $foo;
+ }
+ break;
+ }
+}
+
+switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ if ($something) {
+ echo $string{1};
+ } else if ($else) {
+ switch ($else) {
+ default:
+ }
+ }
+ }
+ break;
+}
+
+switch ($name) {
+ case "1":
+ case "2":
+ case "3":
+ return true;
+}
+
+switch ($name) {
+ case "1":
+ case "2":
+ case "3":
+ default :
+ return true;
+}
+
+function myFunction()
+{
+ ?>
+ <dynamic_content>
+
+ </dynamic_content>
+ <?php
+
+}
+
+switch ($name) {
+ case "1":
+ switch ($name2) {
+ case "1":
+ break;
+ case "2":
+ break;
+ }
+ break;
+ case "2":
+ break;
+}
+
+switch (true) {
+ case true: {
+ }
+ echo 'hi';
+ break;
+ case false:
+ case null:{
+ echo 'hi';
+ }
+ break;
+}
+
+switch (true) {
+ case true: {
+ echo 'hi';
+ }
+ // No break here.
+ case false:
+ case true:
+ case null:{
+ echo 'hi';
+ echo 'hi';
+ }
+ break;
+}
+
+switch (true) {
+ case true: {
+ if (true) {
+ echo 'hi';
+ }
+ }
+ break;
+}
+
+// Testing anon function.
+class a {
+ function c()
+ {
+ $this->doSomething(
+ function () {
+ echo 123;
+ }
+ );
+ }
+}
+
+some_function(
+ function() {
+ $a = 403;
+ if ($a === 404) {
+ $a = 403;
+ }
+ }
+);
+
+some_function(
+ function() {
+ $a = 403;
+ if ($a === 404) {
+ $a = 403;
+ }
+ }
+);
+
+$myFunction = function() {
+ $a = 403;
+ if ($a === 404) {
+ $a = 403;
+ }
+}
+
+class Whatever
+{
+ protected $_protectedArray = array(
+ 'normalString' => 'That email address is already in use!',
+ 'offendingString' => <<<'STRING'
+Each line of this string is always said to be at column 0,
+ no matter how many spaces are placed
+ at the beginning of each line
+and the ending STRING on the next line is reported as having to be indented.
+STRING
+ );
+}
+
+class MyClass
+{
+ public static function myFunction()
+ {
+ if (empty($keywords) === FALSE) {
+ $keywords = 'foo';
+ $existing = 'foo';
+ }
+
+ return $keywords;
+
+ }//end myFunction()
+
+}//end class
+
+$var = call_user_func(
+ $new_var = function () use (&$a) {
+ if ($a > 0) {
+ return $a++;
+ } else {
+ return $a--;
+ }
+ }
+);
+
+class AnonymousFn
+{
+ public function getAnonFn()
+ {
+ return array(
+ 'functions' => Array(
+ 'function1' => function ($a, $b, $c) {
+ $a = $b + $c;
+ $b = $c / 2;
+ return Array($a, $b, $c);
+ },
+ ),
+ );
+ }
+}
+?>
+
+<div>
+ <?php
+ if ($myvar == 'test') {
+ echo 'something';
+ }
+ ?>
+<div>
+<div>
+ <?php
+ if ($myvar == 'test') {
+ echo 'something';
+ }
+ ?>
+<div>
+<div>
+ <?php
+ if ($myvar == 'test') {
+ echo 'something';
+ }
+ ?>
+<div>
+<body>
+ <?php
+ if (isset($_GET["test"])) {
+ if ($_GET["test"] <> "") {
+ $test = true;
+ } else {
+ $test = true;
+ }
+ }
+ ?>
+</body>
+
+<?php
+if (true) {
+ ?>
+ <div>
+ <div>
+ <div>
+ <?php
+ if (true) {
+ echo 'hello';
+ } else {
+ echo 'goodbye';
+ }
+ ?>
+ </div>
+ </div>
+ </div>
+ <?php
+} else {
+ echo 'else';
+}
+?>
+<?php if (isset($param)) { ?>
+ <h3>some text</h3>
+<?php }
+
+$list = [
+ 'fn' => function ($a) {
+ if ($a === true) {
+ echo 'hi';
+ }
+ }
+];
+
+$list = [
+ 'fn' => function ($a) {
+ if ($a === true) {
+ echo 'hi';
+ }
+ }
+];
+
+if ($foo) {
+ foreach ($bar as $baz) {
+ if ($baz) {
+ ?>
+ <div>
+ <div>
+ <div>
+ <?php
+ if ($baz > 1) {
+ echo '1';
+ }
+ ?>
+ </div>
+ <?php
+ if ($baz > 1) {
+ echo '1';
+ }
+ ?>
+ </div>
+ <?php
+ if ($baz > 1) {
+ echo '1';
+ }
+ ?>
+ </div>
+ <?php
+ }
+ }
+}
+
+?>
+<title><?= CHtml::encode($this->pageTitle); ?></title>
+
+<?php
+if ($foo) {
+ echo '1';
+ echo '2';
+ echo '3';
+}
+
+$array = array();
+array_map(
+ function($x)
+ {
+ return trim($x);
+ },
+ $array
+);
+
+function foo(){return function(){};}
+
+$mockedDatabase->expects($this->at(2))
+ ->with($this->callback(
+ function ($subject)
+ {
+ }
+ )
+ );
+
+/** @var Database $mockedDatabase */
+/** @var Container $mockedContainer */
+
+echo $string->append('foo')
+ ->appaend('bar')
+ ->appaend('baz')
+ ->outputUsing(
+ function ()
+ {
+ }
+ );
+
+echo PHP_EOL;
+
+switch ($arg) {
+ case 1:
+ break;
+ case 2:
+ if ($arg2 == 'foo') {
+ }
+ case 3:
+ default:
+ echo 'default';
+}
+
+if ($tokens[$stackPtr]['content']{0} === '#') {
+} else if ($tokens[$stackPtr]['content']{0} === '/'
+ && $tokens[$stackPtr]['content']{1} === '/'
+) {
+}
+
+$var = call_user_func(
+ function() {
+ if ($foo) {
+ $new_var = function() {
+ if ($a > 0) {
+ return $a++;
+ } else {
+ return $a--;
+ }
+ }
+ }
+ }
+);
+
+a(
+ function() {
+ $a = function() {
+ $b = false;
+ };
+ true
+ }
+);
+
+$var = [
+ [
+ '1' =>
+ function () {
+ return true;
+ },
+ ],
+ [
+ '1' =>
+ function () {
+ return true;
+ },
+ '2' => true,
+ ]
+];
+
+if ($foo) {
+ ?>
+ </p>
+ <?php
+}
+
+function foo()
+{
+ $failingTests[$testName][] = array(
+ 'comment' => self::_replaceKeywords($failingComment, $result),
+ 'screenshot' => Test::getScreenshotPath(
+ $projectid,
+ $result['class_name'],
+ ),
+ );
+
+}
+
+$this->mockedDatabase
+ ->with(
+ $this->callback(
+ function () {
+ return;
+ }
+ )
+ );
+
+$this->subject->recordLogin();
+
+function a()
+{
+ if (true) {
+ static::$a[$b] =
+ static::where($c)
+ ->where($c)
+ ->where(
+ function ($d) {
+ $d->whereNull();
+ $d->orWhere();
+ }
+ )
+ ->first();
+
+ if (static::$a[$b] === null) {
+ static::$a[$b] = new static(
+ array(
+ 'a' => $a->id,
+ 'a' => $a->id,
+ )
+ );
+ }
+ }
+
+ return static::$a[$b];
+}
+
+$foo->load(
+ array(
+ 'bar' => function ($baz) {
+ $baz->call();
+ }
+ )
+);
+
+hello();
+
+$foo = array_unique(
+ array_map(
+ function ($entry) {
+ return $entry * 2;
+ },
+ array()
+ )
+);
+bar($foo);
+
+class PHP_CodeSniffer_Tokenizers_JS
+{
+
+ public $scopeOpeners = array(
+ T_CASE => array(
+ 'end' => array(
+ T_BREAK => T_BREAK,
+ T_RETURN => T_RETURN,
+ ),
+ 'strict' => true,
+ ),
+ );
+}
+
+echo $string->
+ append('foo')->
+ appaend('bar')->
+ appaend('baz')->
+ outputUsing(
+ function ()
+ {
+ }
+ );
+
+$str = 'the items I want to show are: ' .
+ implode(
+ ', ',
+ array('a', 'b', 'c')
+ );
+
+echo $str;
+
+$str = 'foo'
+ . '1'
+ . '2';
+
+echo $str;
+
+bar([
+ 'foo' => foo(function () {
+ return 'foo';
+ })
+]);
+
+$domains = array_unique(
+ array_map(
+ function ($url) {
+ $urlObject = new \Purl\Url($url);
+ return $urlObject->registerableDomain;
+ },
+ $sites
+ )
+);
+
+return $domains;
+
+if ($a == 5) :
+ echo "a equals 5";
+ echo "...";
+elseif ($a == 6) :
+ echo "a equals 6";
+ echo "!!!";
+else :
+ echo "a is neither 5 nor 6";
+endif;
+
+if ($foo):
+ if ($bar) $foo = 1;
+ elseif ($baz) $foo = 2;
+endif;
+
+$this
+ ->method(array(
+ 'foo' => 'bar',
+ ), 'arg', array(
+ 'foo' => 'bar',
+ ));
+
+class Foo
+{
+ use Bar {
+ myMethod as renamedMethod;
+ }
+}
+
+class Foo
+{
+ use Bar {
+ myMethod as renamedMethod;
+ }
+}
+
+foo();
+
+array(
+ 'key1' => function ($bar) {
+ return $bar;
+ },
+ 'key2' => function ($foo) {
+ return $foo;
+ },
+);
+
+?>
+<script>
+ var foo = <?php echo bar(
+ 'baz'
+ ); ?>;
+</script>
+<?php
+
+try {
+ echo 'foo';
+} catch (\Exception $e) {
+ echo 'catch';
+} finally {
+ if (false) {
+ echo 'finally false';
+ }
+}
+
+class C0
+{
+ public function m0()
+ {
+ }
+
+ public function m1()
+ {
+ }
+}
+
+namespace Foo {
+
+ use \Foo\Bar;
+ use \Foo\Baz;
+
+ function test() {
+ return true;
+ }
+
+}
+
+declare(ticks=1) {
+ echo 'foo';
+}
+
+$a =
+ (
+ $b = 1
+ );
+$c = 2;
+
+$a =
+ [
+ $b => 1,
+ ];
+$c = 2;
+
+class foo
+{
+ public function get()
+ {
+ $foo = ['b' => 'c',
+ 'd' => [
+ ['e' => 'f']
+ ]];
+ echo '42';
+
+ $foo = array('b' => 'c',
+ 'd' => array(
+ array('e' => 'f')
+ ));
+ echo '42';
+ }
+}
+
+switch ($foo) {
+ case 1:
+ return array();
+ case 2:
+ return '';
+ case 3:
+ return $function();
+ case 4:
+ return $functionCall($param[0]);
+ case 5:
+ return array() + array(); // Array Merge
+ case 6:
+ // String connect
+ return $functionReturningString('') . $functionReturningString(array());
+ case 7:
+ return functionCall(
+ $withMultiLineParam[0],
+ array(),
+ $functionReturningString(
+ $withMultiLineParam[1]
+ )
+ );
+ case 8:
+ return $param[0][0];
+}
+
+class Test {
+
+ public
+ $foo
+ ,$bar
+ ,$baz = [ ]
+ ;
+
+ public function wtfindent() {
+ }
+}
+
+switch ($x) {
+ case 1:
+ return [1];
+ default:
+ return [2];
+}
+
+switch ($foo) {
+ case self::FOO:
+ return $this->bar($gfoo, function ($id) {
+ return FOO::bar($id);
+ }, $values);
+ case self::BAR:
+ $values = $this->bar($foo, $values);
+ break;
+}
+
+$var = array(
+ 'long description' =>
+ array(0, 'something'),
+ 'another long description' =>
+ array(1, "something else")
+);
+
+$services = array(
+ 'service 1' =>
+ Mockery::mock('class 1')
+ ->shouldReceive('setFilter')->once()
+ ->shouldReceive('getNbResults')->atLeast()->once()
+ ->shouldReceive('getSlice')->once()->andReturn(array())
+ ->getMock(),
+ 'service 2' =>
+ Mockery::mock('class 2')
+ ->shouldReceive('__invoke')->once()
+ ->getMock()
+);
+
+class Foo
+{
+ public function setUp()
+ {
+ $this->foo = new class {
+ public $name = 'Some value';
+ };
+ }
+}
+
+try {
+ foo();
+} catch (\Exception $e) {
+ $foo = function() {
+ return 'foo';
+ }
+
+ if (true) {
+ }
+}
+
+if ($foo) {
+ foo();
+} else if (\Exception $e) {
+ $foo = function() {
+ return 'foo';
+ }
+
+ if (true) {
+ }
+} else {
+ $foo = function() {
+ return 'foo';
+ }
+
+ if (true) {
+ }
+}
+
+switch ($parameter) {
+ case null:
+ return [
+ 'foo' => in_array(
+ 'foo',
+ []
+ ),
+ ];
+
+ default:
+ return [];
+}
+
+class SomeClass
+{
+ public function someFunc()
+ {
+ a(function () {
+ echo "a";
+ })->b(function () {
+ echo "b";
+ });
+
+ if (true) {
+ echo "c";
+ }
+ echo "d";
+ }
+}
+
+$params = self::validate_parameters(self::read_competency_framework_parameters(),
+ array(
+ 'id' => $id,
+ ));
+
+$framework = api::read_framework($params['id']);
+self::validate_context($framework->get_context());
+$output = $PAGE->get_renderer('tool_lp');
+
+class Test123
+{
+ protected static
+ $prop1 = [
+ 'testA' => 123,
+ ],
+ $prop2 = [
+ 'testB' => 456,
+ ],
+
+ protected static
+ $prop3 = array(
+ 'testA' => 123,
+ ),
+ $prop4 = array(
+ 'testB' => 456,
+ ),
+
+ protected static $prop5;
+}
+
+$foo = foo(
+ function () {
+ $foo->debug(
+ $a,
+ $b
+ );
+
+ if ($a) {
+ $b = $a;
+ }
+ }
+);
+
+if (somethingIsTrue()) {
+ ?>
+ <div>
+ <?php
+ $someVar = doSomething(
+ 'foobar',
+ true,
+ 'foo',
+ 'bar'
+ );
+
+ if (somethingElseIsTrue()) {
+ doOneThing();
+ } else {
+ doTheOtherThing();
+ }
+ ?>
+ </div>
+ <?php
+}
+
+$foo = [
+ new class() implements Bar {
+
+ public static function foo(): string
+ {
+ return 'foo';
+ }
+ },
+];
+
+$foo = [
+ function () {
+ if ($foo) {
+ return 'foo';
+ }
+ },
+];
+
+$foo
+ ->bar(foo(function () {
+ }), foo(function () {
+ }));
+
+echo 'foo';
+
+class Test {
+
+ public function a() {
+ ?>a<?php
+ }
+
+ public function b(
+ $a
+ ) {
+ echo $a;
+ }
+}
+
+$foo = foo(
+ function () {
+ $foo->debug(
+ $a,
+ $b
+ );
+
+ if ($a) {
+ $b = $a;
+ }
+ }
+);
+
+function test()
+{
+ $array = [];
+ foreach ($array as $data) {
+ [
+ 'key1' => $var1,
+ 'key2' => $var2,
+ ] = $data;
+ foreach ($var1 as $method) {
+ echo $method . $var2;
+ }
+ }
+}
+
+switch ($a) {
+ case 0:
+ function () {
+ };
+ case 1:
+ break;
+}
+
+class Test
+{
+ public function __construct()
+ {
+ if (false) {
+ echo 0;
+ }
+ }
+}
+
+return [
+ 'veryLongKeySoIWantToMakeALineBreak'
+ => 'veryLonValueSoIWantToMakeALineBreak',
+
+ 'someOtherKey' => [
+ 'someValue'
+ ],
+
+ 'arrayWithArraysInThere' => [
+ ['Value1', 'Value1']
+ ],
+];
+
+switch ($sContext) {
+ case 'SOMETHING':
+ case 'CONSTANT':
+ do_something();
+ break;
+ case 'GLOBAL':
+ case 'GLOBAL1':
+ do_something();
+ // Fall through
+ default:
+ {
+ do_something();
+ }
+}
+
+public function foo()
+{
+ $foo('some
+ long description', function () {
+ });
+
+ $foo('some
+ long
+ description', function () {
+ });
+
+ $foo(
+ 'some long description', function () {
+ });
+}
+
+function foo()
+{
+ $foo = array(
+ );
+
+ if ($foo) {
+ echo 'foo';
+ }
+
+ return false;
+}
+
+?>
+<?php
+if (true) {
+}
+else if (true) {
+}
+
+$a = 1;
+
+/*
+$a = array(
+ */
+);
+
+echo ""
--- /dev/null
+@codingStandardsChangeSetting Generic.WhiteSpace.ScopeIndent tabIndent false
+@codingStandardsChangeSetting Generic.WhiteSpace.ScopeIndent exact true
+<?php
+function test()
+{
+ echo 'test';
+ echo 'test2';
+ echo 'test3';
+ if (true) {
+ echo 'test3';
+ }
+ echo 'test3';
+ $x = f1(
+ 'test1', 'test2',
+ 'test3'
+ );
+}
--- /dev/null
+@codingStandardsChangeSetting Generic.WhiteSpace.ScopeIndent tabIndent false
+@codingStandardsChangeSetting Generic.WhiteSpace.ScopeIndent exact true
+<?php
+function test()
+{
+ echo 'test';
+ echo 'test2';
+ echo 'test3';
+ if (true) {
+ echo 'test3';
+ }
+ echo 'test3';
+ $x = f1(
+ 'test1', 'test2',
+ 'test3'
+ );
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ScopeIndent 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\Standards\Generic\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ScopeIndentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Get a list of CLI values to set before the file is tested.
+ *
+ * @param string $testFile The name of the file being tested.
+ * @param \PHP_CodeSniffer\Config $config The config data for the test run.
+ *
+ * @return void
+ */
+ public function setCliValues($testFile, $config)
+ {
+ // Tab width setting is only needed for the tabbed file.
+ if ($testFile === 'ScopeIndentUnitTest.2.inc') {
+ $config->tabWidth = 4;
+ } else {
+ $config->tabWidth = 0;
+ }
+
+ }//end setCliValues()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='ScopeIndentUnitTest.inc')
+ {
+ if ($testFile === 'ScopeIndentUnitTest.1.js') {
+ return array(
+ 6 => 1,
+ 14 => 1,
+ 21 => 1,
+ 30 => 1,
+ 32 => 1,
+ 33 => 1,
+ 34 => 1,
+ 39 => 1,
+ 42 => 1,
+ 59 => 1,
+ 60 => 1,
+ 75 => 1,
+ 120 => 1,
+ 121 => 1,
+ 122 => 1,
+ 123 => 1,
+ 141 => 1,
+ 142 => 1,
+ 155 => 1,
+ 156 => 1,
+ 168 => 1,
+ 184 => 1,
+ );
+ }//end if
+
+ if ($testFile === 'ScopeIndentUnitTest.3.inc') {
+ return array(
+ 6 => 1,
+ 7 => 1,
+ 10 => 1,
+ );
+ }
+
+ return array(
+ 7 => 1,
+ 10 => 1,
+ 13 => 1,
+ 17 => 1,
+ 20 => 1,
+ 24 => 1,
+ 25 => 1,
+ 27 => 1,
+ 28 => 1,
+ 29 => 1,
+ 30 => 1,
+ 58 => 1,
+ 123 => 1,
+ 224 => 1,
+ 225 => 1,
+ 279 => 1,
+ 280 => 1,
+ 281 => 1,
+ 282 => 1,
+ 283 => 1,
+ 284 => 1,
+ 285 => 1,
+ 286 => 1,
+ 336 => 1,
+ 349 => 1,
+ 380 => 1,
+ 386 => 1,
+ 387 => 1,
+ 388 => 1,
+ 389 => 1,
+ 390 => 1,
+ 397 => 1,
+ 419 => 1,
+ 420 => 1,
+ 465 => 1,
+ 467 => 1,
+ 472 => 1,
+ 473 => 1,
+ 474 => 1,
+ 496 => 1,
+ 498 => 1,
+ 500 => 1,
+ 524 => 1,
+ 526 => 1,
+ 544 => 1,
+ 545 => 1,
+ 546 => 1,
+ 639 => 1,
+ 660 => 1,
+ 662 => 1,
+ 802 => 1,
+ 803 => 1,
+ 823 => 1,
+ 858 => 1,
+ 879 => 1,
+ 1163 => 1,
+ 1197 => 1,
+ 1198 => 1,
+ 1243 => 1,
+ 1247 => 1,
+ 1252 => 1,
+ 1254 => 1,
+ 1257 => 1,
+ 1261 => 1,
+ 1262 => 1,
+ 1263 => 1,
+ 1264 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?xml version="1.0"?>
+<ruleset name="Generic">
+ <description>A collection of generic sniffs. This standard is not designed to be used to check code.</description>
+</ruleset>
--- /dev/null
+<?php
+/**
+ * Ensure that browser-specific styles are not used.
+ *
+ * @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\Standards\MySource\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class BrowserSpecificStylesSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+ /**
+ * A list of specific stylesheet suffixes we allow.
+ *
+ * These stylesheets contain browser specific styles
+ * so this sniff ignore them files in the form:
+ * *_moz.css and *_ie7.css etc.
+ *
+ * @var array
+ */
+ protected $specificStylesheets = array(
+ 'moz' => true,
+ 'ie' => true,
+ 'ie7' => true,
+ 'ie8' => true,
+ 'webkit' => true,
+ );
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_STYLE);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ // Ignore files with browser-specific suffixes.
+ $filename = $phpcsFile->getFilename();
+ $breakChar = strrpos($filename, '_');
+ if ($breakChar !== false && substr($filename, -4) === '.css') {
+ $specific = substr($filename, ($breakChar + 1), -4);
+ if (isset($this->specificStylesheets[$specific]) === true) {
+ return;
+ }
+ }
+
+ $tokens = $phpcsFile->getTokens();
+ $content = $tokens[$stackPtr]['content'];
+
+ if ($content{0} === '-') {
+ $error = 'Browser-specific styles are not allowed';
+ $phpcsFile->addError($error, $stackPtr, 'ForbiddenStyle');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that self and static are not used to call public methods in action classes.
+ *
+ * @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\Standards\MySource\Sniffs\Channels;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class DisallowSelfActionsSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_CLASS);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // We are not interested in abstract classes.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($prev !== false && $tokens[$prev]['code'] === T_ABSTRACT) {
+ return;
+ }
+
+ // We are only interested in Action classes.
+ $classNameToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ $className = $tokens[$classNameToken]['content'];
+ if (substr($className, -7) !== 'Actions') {
+ return;
+ }
+
+ $foundFunctions = array();
+ $foundCalls = array();
+
+ // Find all static method calls in the form self::method() in the class.
+ $classEnd = $tokens[$stackPtr]['scope_closer'];
+ for ($i = ($classNameToken + 1); $i < $classEnd; $i++) {
+ if ($tokens[$i]['code'] !== T_DOUBLE_COLON) {
+ if ($tokens[$i]['code'] === T_FUNCTION) {
+ // Cache the function information.
+ $funcName = $phpcsFile->findNext(T_STRING, ($i + 1));
+ $funcScope = $phpcsFile->findPrevious(Tokens::$scopeModifiers, ($i - 1));
+
+ $foundFunctions[$tokens[$funcName]['content']] = strtolower($tokens[$funcScope]['content']);
+ }
+
+ continue;
+ }
+
+ $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, ($i - 1), null, true);
+ if ($tokens[$prevToken]['content'] !== 'self'
+ && $tokens[$prevToken]['content'] !== 'static'
+ ) {
+ continue;
+ }
+
+ $funcNameToken = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), null, true);
+ if ($tokens[$funcNameToken]['code'] === T_VARIABLE) {
+ // We are only interested in function calls.
+ continue;
+ }
+
+ $funcName = $tokens[$funcNameToken]['content'];
+
+ // We've found the function, now we need to find it and see if it is
+ // public, private or protected. If it starts with an underscore we
+ // can assume it is private.
+ if ($funcName{0} === '_') {
+ continue;
+ }
+
+ $foundCalls[$i] = array(
+ 'name' => $funcName,
+ 'type' => strtolower($tokens[$prevToken]['content']),
+ );
+ }//end for
+
+ $errorClassName = substr($className, 0, -7);
+
+ foreach ($foundCalls as $token => $funcData) {
+ if (isset($foundFunctions[$funcData['name']]) === false) {
+ // Function was not in this class, might have come from the parent.
+ // Either way, we can't really check this.
+ continue;
+ } else if ($foundFunctions[$funcData['name']] === 'public') {
+ $type = $funcData['type'];
+ $error = "Static calls to public methods in Action classes must not use the $type keyword; use %s::%s() instead";
+ $data = array(
+ $errorClassName,
+ $funcName,
+ );
+ $phpcsFile->addError($error, $token, 'Found'.ucfirst($funcData['type']), $data);
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that a system does not include itself.
+ *
+ * @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\Standards\MySource\Sniffs\Channels;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class IncludeOwnSystemSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_DOUBLE_COLON);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $fileName = $phpcsFile->getFilename();
+ $matches = array();
+ if (preg_match('|/systems/(.*)/([^/]+)?actions.inc$|i', $fileName, $matches) === 0) {
+ // Not an actions file.
+ return;
+ }
+
+ $ownClass = $matches[2];
+ $tokens = $phpcsFile->getTokens();
+
+ $typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($stackPtr + 2), null, false, true);
+ $typeName = trim($tokens[$typeName]['content'], " '");
+ switch (strtolower($tokens[($stackPtr + 1)]['content'])) {
+ case 'includesystem' :
+ $included = strtolower($typeName);
+ break;
+ case 'includeasset' :
+ $included = strtolower($typeName).'assettype';
+ break;
+ case 'includewidget' :
+ $included = strtolower($typeName).'widgettype';
+ break;
+ default:
+ return;
+ }
+
+ if ($included === strtolower($ownClass)) {
+ $error = "You do not need to include \"%s\" from within the system's own actions file";
+ $data = array($ownClass);
+ $phpcsFile->addError($error, $stackPtr, 'NotRequired', $data);
+ }
+
+ }//end process()
+
+
+ /**
+ * Determines the included class name from given token.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param array $tokens The array of file tokens.
+ * @param int $stackPtr The position in the tokens array of the
+ * potentially included class.
+ *
+ * @return string
+ */
+ protected function getIncludedClassFromToken(
+ $phpcsFile,
+ array $tokens,
+ $stackPtr
+ ) {
+
+ return false;
+
+ }//end getIncludedClassFromToken()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that systems, asset types and libs are included before they are used.
+ *
+ * @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\Standards\MySource\Sniffs\Channels;
+
+use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class IncludeSystemSniff extends AbstractScopeSniff
+{
+
+ /**
+ * A list of classes that don't need to be included.
+ *
+ * @var string[]
+ */
+ private $ignore = array(
+ 'self' => true,
+ 'static' => true,
+ 'parent' => true,
+ 'channels' => true,
+ 'basesystem' => true,
+ 'dal' => true,
+ 'init' => true,
+ 'pdo' => true,
+ 'util' => true,
+ 'ziparchive' => true,
+ 'phpunit_framework_assert' => true,
+ 'abstractmysourceunittest' => true,
+ 'abstractdatacleanunittest' => true,
+ 'exception' => true,
+ 'abstractwidgetwidgettype' => true,
+ 'domdocument' => true,
+ );
+
+
+ /**
+ * Constructs a Squiz_Sniffs_Scope_MethodScopeSniff.
+ */
+ public function __construct()
+ {
+ parent::__construct(array(T_FUNCTION), array(T_DOUBLE_COLON, T_EXTENDS), true);
+
+ }//end __construct()
+
+
+ /**
+ * Processes the function tokens within the class.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param integer $stackPtr The position where the token was found.
+ * @param integer $currScope The current scope opener token.
+ *
+ * @return void
+ */
+ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Determine the name of the class that the static function
+ // is being called on.
+ $classNameToken = $phpcsFile->findPrevious(
+ T_WHITESPACE,
+ ($stackPtr - 1),
+ null,
+ true
+ );
+
+ // Don't process class names represented by variables as this can be
+ // an inexact science.
+ if ($tokens[$classNameToken]['code'] === T_VARIABLE) {
+ return;
+ }
+
+ $className = $tokens[$classNameToken]['content'];
+ if (isset($this->ignore[strtolower($className)]) === true) {
+ return;
+ }
+
+ $includedClasses = array();
+
+ $fileName = strtolower($phpcsFile->getFilename());
+ $matches = array();
+ if (preg_match('|/systems/(.*)/([^/]+)?actions.inc$|', $fileName, $matches) !== 0) {
+ // This is an actions file, which means we don't
+ // have to include the system in which it exists.
+ $includedClasses[$matches[2]] = true;
+
+ // Or a system it implements.
+ $class = $phpcsFile->getCondition($stackPtr, T_CLASS);
+ $implements = $phpcsFile->findNext(T_IMPLEMENTS, $class, ($class + 10));
+ if ($implements !== false) {
+ $implementsClass = $phpcsFile->findNext(T_STRING, $implements);
+ $implementsClassName = strtolower($tokens[$implementsClass]['content']);
+ if (substr($implementsClassName, -7) === 'actions') {
+ $includedClasses[substr($implementsClassName, 0, -7)] = true;
+ }
+ }
+ }
+
+ // Go searching for includeSystem and includeAsset calls within this
+ // function, or the inclusion of .inc files, which
+ // would be library files.
+ for ($i = ($currScope + 1); $i < $stackPtr; $i++) {
+ $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
+ if ($name !== false) {
+ $includedClasses[$name] = true;
+ // Special case for Widgets cause they are, well, special.
+ } else if (strtolower($tokens[$i]['content']) === 'includewidget') {
+ $typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($i + 1));
+ $typeName = trim($tokens[$typeName]['content'], " '");
+ $includedClasses[strtolower($typeName).'widgettype'] = true;
+ }
+ }
+
+ // Now go searching for includeSystem, includeAsset or require/include
+ // calls outside our scope. If we are in a class, look outside the
+ // class. If we are not, look outside the function.
+ $condPtr = $currScope;
+ if ($phpcsFile->hasCondition($stackPtr, T_CLASS) === true) {
+ foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condType) {
+ if ($condType === T_CLASS) {
+ break;
+ }
+ }
+ }
+
+ for ($i = 0; $i < $condPtr; $i++) {
+ // Skip other scopes.
+ if (isset($tokens[$i]['scope_closer']) === true) {
+ $i = $tokens[$i]['scope_closer'];
+ continue;
+ }
+
+ $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
+ if ($name !== false) {
+ $includedClasses[$name] = true;
+ }
+ }
+
+ // If we are in a testing class, we might have also included
+ // some systems and classes in our setUp() method.
+ $setupFunction = null;
+ if ($phpcsFile->hasCondition($stackPtr, T_CLASS) === true) {
+ foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condType) {
+ if ($condType === T_CLASS) {
+ // Is this is a testing class?
+ $name = $phpcsFile->findNext(T_STRING, $condPtr);
+ $name = $tokens[$name]['content'];
+ if (substr($name, -8) === 'UnitTest') {
+ // Look for a method called setUp().
+ $end = $tokens[$condPtr]['scope_closer'];
+ $function = $phpcsFile->findNext(T_FUNCTION, ($condPtr + 1), $end);
+ while ($function !== false) {
+ $name = $phpcsFile->findNext(T_STRING, $function);
+ if ($tokens[$name]['content'] === 'setUp') {
+ $setupFunction = $function;
+ break;
+ }
+
+ $function = $phpcsFile->findNext(T_FUNCTION, ($function + 1), $end);
+ }
+ }
+ }
+ }//end foreach
+ }//end if
+
+ if ($setupFunction !== null) {
+ $start = ($tokens[$setupFunction]['scope_opener'] + 1);
+ $end = $tokens[$setupFunction]['scope_closer'];
+ for ($i = $start; $i < $end; $i++) {
+ $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
+ if ($name !== false) {
+ $includedClasses[$name] = true;
+ }
+ }
+ }//end if
+
+ if (isset($includedClasses[strtolower($className)]) === false) {
+ $error = 'Static method called on non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once';
+ $data = array($className);
+ $phpcsFile->addError($error, $stackPtr, 'NotIncludedCall', $data);
+ }
+
+ }//end processTokenWithinScope()
+
+
+ /**
+ * Processes a token within the scope that this test is listening to.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * this token was found.
+ *
+ * @return void
+ */
+ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['code'] === T_EXTENDS) {
+ // Find the class name.
+ $classNameToken = $phpcsFile->findNext(T_STRING, ($stackPtr + 1));
+ $className = $tokens[$classNameToken]['content'];
+ } else {
+ // Determine the name of the class that the static function
+ // is being called on. But don't process class names represented by
+ // variables as this can be an inexact science.
+ $classNameToken = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($tokens[$classNameToken]['code'] === T_VARIABLE) {
+ return;
+ }
+
+ $className = $tokens[$classNameToken]['content'];
+ }
+
+ // Some systems are always available.
+ if (isset($this->ignore[strtolower($className)]) === true) {
+ return;
+ }
+
+ $includedClasses = array();
+
+ $fileName = strtolower($phpcsFile->getFilename());
+ $matches = array();
+ if (preg_match('|/systems/([^/]+)/([^/]+)?actions.inc$|', $fileName, $matches) !== 0) {
+ // This is an actions file, which means we don't
+ // have to include the system in which it exists
+ // We know the system from the path.
+ $includedClasses[$matches[1]] = true;
+ }
+
+ // Go searching for includeSystem, includeAsset or require/include
+ // calls outside our scope.
+ for ($i = 0; $i < $stackPtr; $i++) {
+ // Skip classes and functions as will we never get
+ // into their scopes when including this file, although
+ // we have a chance of getting into IF's, WHILE's etc.
+ if (($tokens[$i]['code'] === T_CLASS
+ || $tokens[$i]['code'] === T_INTERFACE
+ || $tokens[$i]['code'] === T_FUNCTION)
+ && isset($tokens[$i]['scope_closer']) === true
+ ) {
+ $i = $tokens[$i]['scope_closer'];
+ continue;
+ }
+
+ $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
+ if ($name !== false) {
+ $includedClasses[$name] = true;
+ // Special case for Widgets cause they are, well, special.
+ } else if (strtolower($tokens[$i]['content']) === 'includewidget') {
+ $typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($i + 1));
+ $typeName = trim($tokens[$typeName]['content'], " '");
+ $includedClasses[strtolower($typeName).'widgettype'] = true;
+ }
+ }//end for
+
+ if (isset($includedClasses[strtolower($className)]) === false) {
+ if ($tokens[$stackPtr]['code'] === T_EXTENDS) {
+ $error = 'Class extends non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once';
+ $data = array($className);
+ $phpcsFile->addError($error, $stackPtr, 'NotIncludedExtends', $data);
+ } else {
+ $error = 'Static method called on non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once';
+ $data = array($className);
+ $phpcsFile->addError($error, $stackPtr, 'NotIncludedCall', $data);
+ }
+ }
+
+ }//end processTokenOutsideScope()
+
+
+ /**
+ * Determines the included class name from given token.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param array $tokens The array of file tokens.
+ * @param int $stackPtr The position in the tokens array of the
+ * potentially included class.
+ *
+ * @return string
+ */
+ protected function getIncludedClassFromToken(File $phpcsFile, array $tokens, $stackPtr)
+ {
+ if (strtolower($tokens[$stackPtr]['content']) === 'includesystem') {
+ $systemName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($stackPtr + 1));
+ $systemName = trim($tokens[$systemName]['content'], " '");
+ return strtolower($systemName);
+ } else if (strtolower($tokens[$stackPtr]['content']) === 'includeasset') {
+ $typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($stackPtr + 1));
+ $typeName = trim($tokens[$typeName]['content'], " '");
+ return strtolower($typeName).'assettype';
+ } else if (isset(Tokens::$includeTokens[$tokens[$stackPtr]['code']]) === true) {
+ $filePath = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($stackPtr + 1));
+ $filePath = $tokens[$filePath]['content'];
+ $filePath = trim($filePath, " '");
+ $filePath = basename($filePath, '.inc');
+ return strtolower($filePath);
+ }
+
+ return false;
+
+ }//end getIncludedClassFromToken()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that systems and asset types are used if they are included.
+ *
+ * @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\Standards\MySource\Sniffs\Channels;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class UnusedSystemSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_DOUBLE_COLON);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // Check if this is a call to includeSystem, includeAsset or includeWidget.
+ $methodName = strtolower($tokens[($stackPtr + 1)]['content']);
+ if ($methodName === 'includesystem'
+ || $methodName === 'includeasset'
+ || $methodName === 'includewidget'
+ ) {
+ $systemName = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 3), null, true);
+ if ($systemName === false || $tokens[$systemName]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
+ // Must be using a variable instead of a specific system name.
+ // We can't accurately check that.
+ return;
+ }
+
+ $systemName = trim($tokens[$systemName]['content'], " '");
+ } else {
+ return;
+ }
+
+ if ($methodName === 'includeasset') {
+ $systemName .= 'assettype';
+ } else if ($methodName === 'includewidget') {
+ $systemName .= 'widgettype';
+ }
+
+ $systemName = strtolower($systemName);
+
+ // Now check if this system is used anywhere in this scope.
+ $level = $tokens[$stackPtr]['level'];
+ for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) {
+ if ($tokens[$i]['level'] < $level) {
+ // We have gone out of scope.
+ // If the original include was inside an IF statement that
+ // is checking if the system exists, check the outer scope
+ // as well.
+ if ($tokens[$stackPtr]['level'] === $level) {
+ // We are still in the base level, so this is the first
+ // time we have got here.
+ $conditions = array_keys($tokens[$stackPtr]['conditions']);
+ if (empty($conditions) === false) {
+ $cond = array_pop($conditions);
+ if ($tokens[$cond]['code'] === T_IF) {
+ $i = $tokens[$cond]['scope_closer'];
+ $level--;
+ continue;
+ }
+ }
+ }
+
+ break;
+ }//end if
+
+ if ($tokens[$i]['code'] !== T_DOUBLE_COLON
+ && $tokens[$i]['code'] !== T_EXTENDS
+ && $tokens[$i]['code'] !== T_IMPLEMENTS
+ ) {
+ continue;
+ }
+
+ switch ($tokens[$i]['code']) {
+ case T_DOUBLE_COLON:
+ $usedName = strtolower($tokens[($i - 1)]['content']);
+ if ($usedName === $systemName) {
+ // The included system was used, so it is fine.
+ return;
+ }
+ break;
+ case T_EXTENDS:
+ $classNameToken = $phpcsFile->findNext(T_STRING, ($i + 1));
+ $className = strtolower($tokens[$classNameToken]['content']);
+ if ($className === $systemName) {
+ // The included system was used, so it is fine.
+ return;
+ }
+ break;
+ case T_IMPLEMENTS:
+ $endImplements = $phpcsFile->findNext(array(T_EXTENDS, T_OPEN_CURLY_BRACKET), ($i + 1));
+ for ($x = ($i + 1); $x < $endImplements; $x++) {
+ if ($tokens[$x]['code'] === T_STRING) {
+ $className = strtolower($tokens[$x]['content']);
+ if ($className === $systemName) {
+ // The included system was used, so it is fine.
+ return;
+ }
+ }
+ }
+ break;
+ }//end switch
+ }//end for
+
+ // If we get to here, the system was not use.
+ $error = 'Included system "%s" is never used';
+ $data = array($systemName);
+ $phpcsFile->addError($error, $stackPtr, 'Found', $data);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Parses and verifies the doc comments for functions.
+ *
+ * Same as the Squiz standard, but adds support for API tags.
+ *
+ * @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\Standards\MySource\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Standards\Squiz\Sniffs\Commenting\FunctionCommentSniff as SquizFunctionCommentSniff;
+use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Files\File;
+
+class FunctionCommentSniff extends SquizFunctionCommentSniff
+{
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ parent::process($phpcsFile, $stackPtr);
+
+ $tokens = $phpcsFile->getTokens();
+ $find = Tokens::$methodPrefixes;
+ $find[] = T_WHITESPACE;
+
+ $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
+ if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG) {
+ return;
+ }
+
+ $commentStart = $tokens[$commentEnd]['comment_opener'];
+ $hasApiTag = false;
+ foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
+ if ($tokens[$tag]['content'] === '@api') {
+ if ($hasApiTag === true) {
+ // We've come across an API tag already, which means
+ // we were not the first tag in the API list.
+ $error = 'The @api tag must come first in the @api tag list in a function comment';
+ $phpcsFile->addError($error, $tag, 'ApiNotFirst');
+ }
+
+ $hasApiTag = true;
+
+ // There needs to be a blank line before the @api tag.
+ $prev = $phpcsFile->findPrevious(array(T_DOC_COMMENT_STRING, T_DOC_COMMENT_TAG), ($tag - 1));
+ if ($tokens[$prev]['line'] !== ($tokens[$tag]['line'] - 2)) {
+ $error = 'There must be one blank line before the @api tag in a function comment';
+ $phpcsFile->addError($error, $tag, 'ApiSpacing');
+ }
+ } else if (substr($tokens[$tag]['content'], 0, 5) === '@api-') {
+ $hasApiTag = true;
+
+ $prev = $phpcsFile->findPrevious(array(T_DOC_COMMENT_STRING, T_DOC_COMMENT_TAG), ($tag - 1));
+ if ($tokens[$prev]['line'] !== ($tokens[$tag]['line'] - 1)) {
+ $error = 'There must be no blank line before the @%s tag in a function comment';
+ $data = array($tokens[$tag]['content']);
+ $phpcsFile->addError($error, $tag, 'ApiTagSpacing', $data);
+ }
+ }//end if
+ }//end foreach
+
+ if ($hasApiTag === true && substr($tokens[$tag]['content'], 0, 4) !== '@api') {
+ // API tags must be the last tags in a function comment.
+ $error = 'The @api tags must be the last tags in a function comment';
+ $phpcsFile->addError($error, $commentEnd, 'ApiNotLast');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Warns about the use of debug 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\Standards\MySource\Sniffs\Debug;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DebugCodeSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_DOUBLE_COLON);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $className = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if (strtolower($tokens[$className]['content']) === 'debug') {
+ $method = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ $error = 'Call to debug function Debug::%s() must be removed';
+ $data = array($tokens[$method]['content']);
+ $phpcsFile->addError($error, $stackPtr, 'Found', $data);
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that console is not used for function or var names.
+ *
+ * @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\Standards\MySource\Sniffs\Debug;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class FirebugConsoleSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_STRING,
+ T_PROPERTY,
+ T_LABEL,
+ T_OBJECT,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (strtolower($tokens[$stackPtr]['content']) === 'console') {
+ $error = 'Variables, functions and labels must not be named "console"; name may conflict with Firebug internal variable';
+ $phpcsFile->addError($error, $stackPtr, 'ConflictFound');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures this is not assigned to any other var but self.
+ *
+ * @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\Standards\MySource\Sniffs\Objects;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class AssignThisSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_THIS);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // Ignore this.something and other uses of "this" that are not
+ // direct assignments.
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$next]['code'] !== T_SEMICOLON) {
+ if ($tokens[$next]['line'] === $tokens[$stackPtr]['line']) {
+ return;
+ }
+ }
+
+ // Something must be assigned to "this".
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] !== T_EQUAL) {
+ return;
+ }
+
+ // A variable needs to be assigned to "this".
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($prev - 1), null, true);
+ if ($tokens[$prev]['code'] !== T_STRING) {
+ return;
+ }
+
+ // We can only assign "this" to a var called "self".
+ if ($tokens[$prev]['content'] !== 'self' && $tokens[$prev]['content'] !== '_self') {
+ $error = 'Keyword "this" can only be assigned to a variable called "self" or "_self"';
+ $phpcsFile->addError($error, $prev, 'NotSelf');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures the create() method of widget types properly uses callbacks.
+ *
+ * @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\Standards\MySource\Sniffs\Objects;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class CreateWidgetTypeCallbackSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OBJECT);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $className = $phpcsFile->findPrevious(T_STRING, ($stackPtr - 1));
+ if (substr(strtolower($tokens[$className]['content']), -10) !== 'widgettype') {
+ return;
+ }
+
+ // Search for a create method.
+ $create = $phpcsFile->findNext(T_PROPERTY, $stackPtr, $tokens[$stackPtr]['bracket_closer'], null, 'create');
+ if ($create === false) {
+ return;
+ }
+
+ $function = $phpcsFile->findNext(array(T_WHITESPACE, T_COLON), ($create + 1), null, true);
+ if ($tokens[$function]['code'] !== T_FUNCTION
+ && $tokens[$function]['code'] !== T_CLOSURE
+ ) {
+ return;
+ }
+
+ $start = ($tokens[$function]['scope_opener'] + 1);
+ $end = ($tokens[$function]['scope_closer'] - 1);
+
+ // Check that the first argument is called "callback".
+ $arg = $phpcsFile->findNext(T_WHITESPACE, ($tokens[$function]['parenthesis_opener'] + 1), null, true);
+ if ($tokens[$arg]['content'] !== 'callback') {
+ $error = 'The first argument of the create() method of a widget type must be called "callback"';
+ $phpcsFile->addError($error, $arg, 'FirstArgNotCallback');
+ }
+
+ /*
+ Look for return statements within the function. They cannot return
+ anything and must be preceded by the callback.call() line. The
+ callback itself must contain "self" or "this" as the first argument
+ and there needs to be a call to the callback function somewhere
+ in the create method. All calls to the callback function must be
+ followed by a return statement or the end of the method.
+ */
+
+ $foundCallback = false;
+ $passedCallback = false;
+ $nestedFunction = null;
+ for ($i = $start; $i <= $end; $i++) {
+ // Keep track of nested functions.
+ if ($nestedFunction !== null) {
+ if ($i === $nestedFunction) {
+ $nestedFunction = null;
+ continue;
+ }
+ } else if (($tokens[$i]['code'] === T_FUNCTION
+ || $tokens[$i]['code'] === T_CLOSURE)
+ && isset($tokens[$i]['scope_closer']) === true
+ ) {
+ $nestedFunction = $tokens[$i]['scope_closer'];
+ continue;
+ }
+
+ if ($nestedFunction === null && $tokens[$i]['code'] === T_RETURN) {
+ // Make sure return statements are not returning anything.
+ if ($tokens[($i + 1)]['code'] !== T_SEMICOLON) {
+ $error = 'The create() method of a widget type must not return a value';
+ $phpcsFile->addError($error, $i, 'ReturnValue');
+ }
+
+ continue;
+ } else if ($tokens[$i]['code'] !== T_STRING
+ || $tokens[$i]['content'] !== 'callback'
+ ) {
+ continue;
+ }
+
+ // If this is the form "callback.call(" then it is a call
+ // to the callback function.
+ if ($tokens[($i + 1)]['code'] !== T_OBJECT_OPERATOR
+ || $tokens[($i + 2)]['content'] !== 'call'
+ || $tokens[($i + 3)]['code'] !== T_OPEN_PARENTHESIS
+ ) {
+ // One last chance; this might be the callback function
+ // being passed to another function, like this
+ // "this.init(something, callback, something)".
+ if (isset($tokens[$i]['nested_parenthesis']) === false) {
+ continue;
+ }
+
+ // Just make sure those brackets dont belong to anyone,
+ // like an IF or FOR statement.
+ foreach ($tokens[$i]['nested_parenthesis'] as $bracket) {
+ if (isset($tokens[$bracket]['parenthesis_owner']) === true) {
+ continue(2);
+ }
+ }
+
+ // Note that we use this endBracket down further when checking
+ // for a RETURN statement.
+ $endBracket = end($tokens[$i]['nested_parenthesis']);
+ $bracket = key($tokens[$i]['nested_parenthesis']);
+
+ $prev = $phpcsFile->findPrevious(
+ Tokens::$emptyTokens,
+ ($bracket - 1),
+ null,
+ true
+ );
+
+ if ($tokens[$prev]['code'] !== T_STRING) {
+ // This is not a function passing the callback.
+ continue;
+ }
+
+ $passedCallback = true;
+ }//end if
+
+ $foundCallback = true;
+
+ if ($passedCallback === false) {
+ // The first argument must be "this" or "self".
+ $arg = $phpcsFile->findNext(T_WHITESPACE, ($i + 4), null, true);
+ if ($tokens[$arg]['content'] !== 'this'
+ && $tokens[$arg]['content'] !== 'self'
+ ) {
+ $error = 'The first argument passed to the callback function must be "this" or "self"';
+ $phpcsFile->addError($error, $arg, 'FirstArgNotSelf');
+ }
+ }
+
+ // Now it must be followed by a return statement or the end of the function.
+ if ($passedCallback === false) {
+ $endBracket = $tokens[($i + 3)]['parenthesis_closer'];
+ }
+
+ for ($next = $endBracket; $next <= $end; $next++) {
+ // Skip whitespace so we find the next content after the call.
+ if (isset(Tokens::$emptyTokens[$tokens[$next]['code']]) === true) {
+ continue;
+ }
+
+ // Skip closing braces like END IF because it is not executable code.
+ if ($tokens[$next]['code'] === T_CLOSE_CURLY_BRACKET) {
+ continue;
+ }
+
+ // We don't care about anything on the current line, like a
+ // semicolon. It doesn't matter if there are other statements on the
+ // line because another sniff will check for those.
+ if ($tokens[$next]['line'] === $tokens[$endBracket]['line']) {
+ continue;
+ }
+
+ break;
+ }
+
+ if ($next !== $tokens[$function]['scope_closer']
+ && $tokens[$next]['code'] !== T_RETURN
+ ) {
+ $error = 'The call to the callback function must be followed by a return statement if it is not the last statement in the create() method';
+ $phpcsFile->addError($error, $i, 'NoReturn');
+ }
+ }//end for
+
+ if ($foundCallback === false) {
+ $error = 'The create() method of a widget type must call the callback function';
+ $phpcsFile->addError($error, $create, 'CallbackNotCalled');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that widgets are not manually created.
+ *
+ * @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\Standards\MySource\Sniffs\Objects;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DisallowNewWidgetSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_NEW);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $className = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$className]['code'] !== T_STRING) {
+ return;
+ }
+
+ if (substr(strtolower($tokens[$className]['content']), -10) === 'widgettype') {
+ $widgetType = substr($tokens[$className]['content'], 0, -10);
+ $error = 'Manual creation of widget objects is banned; use Widget::getWidget(\'%s\'); instead';
+ $data = array($widgetType);
+ $phpcsFile->addError($error, $stackPtr, 'Found', $data);
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that values submitted via JS are not compared to NULL.
+ *
+ * With jQuery 1.8, the behavior of ajax requests changed so that null values are
+ * submitted as null= instead of null=null.
+ *
+ * @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\Standards\MySource\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class AjaxNullComparisonSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_FUNCTION);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // Make sure it is an API function. We know this by the doc comment.
+ $commentEnd = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $stackPtr);
+ $commentStart = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, ($commentEnd - 1));
+ $comment = $phpcsFile->getTokensAsString($commentStart, ($commentEnd - $commentStart));
+ if (strpos($comment, '* @api') === false) {
+ return;
+ }
+
+ // Find all the vars passed in as we are only interested in comparisons
+ // to NULL for these specific variables.
+ $foundVars = array();
+ $open = $tokens[$stackPtr]['parenthesis_opener'];
+ $close = $tokens[$stackPtr]['parenthesis_closer'];
+ for ($i = ($open + 1); $i < $close; $i++) {
+ if ($tokens[$i]['code'] === T_VARIABLE) {
+ $foundVars[$tokens[$i]['content']] = true;
+ }
+ }
+
+ if (empty($foundVars) === true) {
+ return;
+ }
+
+ $start = $tokens[$stackPtr]['scope_opener'];
+ $end = $tokens[$stackPtr]['scope_closer'];
+ for ($i = ($start + 1); $i < $end; $i++) {
+ if ($tokens[$i]['code'] !== T_VARIABLE
+ || isset($foundVars[$tokens[$i]['content']]) === false
+ ) {
+ continue;
+ }
+
+ $operator = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), null, true);
+ if ($tokens[$operator]['code'] !== T_IS_IDENTICAL
+ && $tokens[$operator]['code'] !== T_IS_NOT_IDENTICAL
+ ) {
+ continue;
+ }
+
+ $nullValue = $phpcsFile->findNext(T_WHITESPACE, ($operator + 1), null, true);
+ if ($tokens[$nullValue]['code'] !== T_NULL) {
+ continue;
+ }
+
+ $error = 'Values submitted via Ajax requests should not be compared directly to NULL; use empty() instead';
+ $phpcsFile->addWarning($error, $nullValue, 'Found');
+ }//end for
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that eval() is not used to create objects.
+ *
+ * @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\Standards\MySource\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class EvalObjectFactorySniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_EVAL);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ /*
+ We need to find all strings that will be in the eval
+ to determine if the "new" keyword is being used.
+ */
+
+ $openBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($stackPtr + 1));
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+
+ $strings = array();
+ $vars = array();
+
+ for ($i = ($openBracket + 1); $i < $closeBracket; $i++) {
+ if (isset(Tokens::$stringTokens[$tokens[$i]['code']]) === true) {
+ $strings[$i] = $tokens[$i]['content'];
+ } else if ($tokens[$i]['code'] === T_VARIABLE) {
+ $vars[$i] = $tokens[$i]['content'];
+ }
+ }
+
+ /*
+ We now have some variables that we need to expand into
+ the strings that were assigned to them, if any.
+ */
+
+ foreach ($vars as $varPtr => $varName) {
+ while (($prev = $phpcsFile->findPrevious(T_VARIABLE, ($varPtr - 1))) !== false) {
+ // Make sure this is an assignment of the variable. That means
+ // it will be the first thing on the line.
+ $prevContent = $phpcsFile->findPrevious(T_WHITESPACE, ($prev - 1), null, true);
+ if ($tokens[$prevContent]['line'] === $tokens[$prev]['line']) {
+ $varPtr = $prevContent;
+ continue;
+ }
+
+ if ($tokens[$prev]['content'] !== $varName) {
+ // This variable has a different name.
+ $varPtr = $prevContent;
+ continue;
+ }
+
+ // We found one.
+ break;
+ }//end while
+
+ if ($prev !== false) {
+ // Find all strings on the line.
+ $lineEnd = $phpcsFile->findNext(T_SEMICOLON, ($prev + 1));
+ for ($i = ($prev + 1); $i < $lineEnd; $i++) {
+ if (isset(Tokens::$stringTokens[$tokens[$i]['code']]) === true) {
+ $strings[$i] = $tokens[$i]['content'];
+ }
+ }
+ }
+ }//end foreach
+
+ foreach ($strings as $string) {
+ // If the string has "new" in it, it is not allowed.
+ // We don't bother checking if the word "new" is echo'd
+ // because that is unlikely to happen. We assume the use
+ // of "new" is for object instantiation.
+ if (strstr($string, ' new ') !== false) {
+ $error = 'Do not use eval() to create objects dynamically; use reflection instead';
+ $phpcsFile->addWarning($error, $stackPtr, 'Found');
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that getRequestData() is used to access super globals.
+ *
+ * @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\Standards\MySource\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class GetRequestDataSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_VARIABLE);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $varName = $tokens[$stackPtr]['content'];
+ if ($varName !== '$_REQUEST'
+ && $varName !== '$_GET'
+ && $varName !== '$_POST'
+ && $varName !== '$_FILES'
+ ) {
+ return;
+ }
+
+ // The only place these super globals can be accessed directly is
+ // in the getRequestData() method of the Security class.
+ $inClass = false;
+ foreach ($tokens[$stackPtr]['conditions'] as $i => $type) {
+ if ($tokens[$i]['code'] === T_CLASS) {
+ $className = $phpcsFile->findNext(T_STRING, $i);
+ $className = $tokens[$className]['content'];
+ if (strtolower($className) === 'security') {
+ $inClass = true;
+ } else {
+ // We don't have nested classes.
+ break;
+ }
+ } else if ($inClass === true && $tokens[$i]['code'] === T_FUNCTION) {
+ $funcName = $phpcsFile->findNext(T_STRING, $i);
+ $funcName = $tokens[$funcName]['content'];
+ if (strtolower($funcName) === 'getrequestdata') {
+ // This is valid.
+ return;
+ } else {
+ // We don't have nested functions.
+ break;
+ }
+ }//end if
+ }//end foreach
+
+ // If we get to here, the super global was used incorrectly.
+ // First find out how it is being used.
+ $globalName = strtolower(substr($varName, 2));
+ $usedVar = '';
+
+ $openBracket = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$openBracket]['code'] === T_OPEN_SQUARE_BRACKET) {
+ $closeBracket = $tokens[$openBracket]['bracket_closer'];
+ $usedVar = $phpcsFile->getTokensAsString(($openBracket + 1), ($closeBracket - $openBracket - 1));
+ }
+
+ $type = 'SuperglobalAccessed';
+ $error = 'The %s super global must not be accessed directly; use Security::getRequestData(';
+ $data = array($varName);
+ if ($usedVar !== '') {
+ $type .= 'WithVar';
+ $error .= '%s, \'%s\'';
+ $data[] = $usedVar;
+ $data[] = $globalName;
+ }
+
+ $error .= ') instead';
+ $phpcsFile->addError($error, $stackPtr, $type, $data);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Warns when function values are returned directly.
+ *
+ * @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\Standards\MySource\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ReturnFunctionValueSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_RETURN);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $functionName = $phpcsFile->findNext(T_STRING, ($stackPtr + 1), null, false, null, true);
+
+ while ($functionName !== false) {
+ // Check if this is really a function.
+ $bracket = $phpcsFile->findNext(T_WHITESPACE, ($functionName + 1), null, true);
+ if ($tokens[$bracket]['code'] !== T_OPEN_PARENTHESIS) {
+ // Not a function call.
+ $functionName = $phpcsFile->findNext(T_STRING, ($functionName + 1), null, false, null, true);
+ continue;
+ }
+
+ $error = 'The result of a function call should be assigned to a variable before being returned';
+ $phpcsFile->addWarning($error, $stackPtr, 'NotAssigned');
+ break;
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that strings are not joined using array.join().
+ *
+ * @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\Standards\MySource\Sniffs\Strings;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class JoinStringsSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_STRING);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param integer $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]['content'] !== 'join') {
+ return;
+ }
+
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] !== T_OBJECT_OPERATOR) {
+ return;
+ }
+
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev - 1), null, true);
+ if ($tokens[$prev]['code'] === T_CLOSE_SQUARE_BRACKET) {
+ $opener = $tokens[$prev]['bracket_opener'];
+ if ($tokens[($opener - 1)]['code'] !== T_STRING) {
+ // This means the array is declared inline, like x = [a,b,c].join()
+ // and not elsewhere, like x = y[a].join()
+ // The first is not allowed while the second is.
+ $error = 'Joining strings using inline arrays is not allowed; use the + operator instead';
+ $phpcsFile->addError($error, $stackPtr, 'ArrayNotAllowed');
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+.SettingsTabPaneWidgetType-tab-mid {
+ background: transparent url(tab_inact_mid.png) repeat-x;
+ line-height: -25px;
+ cursor: pointer;
+ -moz-user-select: none;
+}
+
+.AssetLineageWidgetType-item {
+ float: left;
+ list-style: none;
+ height: 22px;
+ cursor: pointer;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the BrowserSpecificStyles 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\Standards\MySource\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class BrowserSpecificStylesUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(5 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class SomethingActions
+{
+
+ private static function _x()
+ {
+ }
+
+
+ public static function y()
+ {
+ self::z();
+ static::z();
+ SomethingActions::z();
+ static::_x();
+ self::a();
+ static::a();
+ }
+
+
+ public static function z()
+ {
+ }
+
+ protected static function a()
+ {
+ self::a(); // recursion, yay!
+ self::z();
+ static::y();
+ self::b();
+ echo self::$_myVar;
+ echo static::$yourVar;
+ }
+}
+
+abstract class AbstractEditingScreenModeWidgetActions extends AbstractEditingModeWidgetActions {
+
+ public static function getScreens($systemName)
+ {
+
+ }//end getScreens()
+
+ public static function setHelpScreenTitle()
+ {
+ // This is allowed because we are in an abstract class.
+ $screens = self::getScreens('');
+
+ }//end setHelpScreenTitle()
+
+}//end class
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowSelfActions 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\Standards\MySource\Tests\Channels;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowSelfActionsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 12 => 1,
+ 13 => 1,
+ 28 => 1,
+ 29 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+Channels::includeSystem('MySystem');
+Channels::includeAsset('Page');
+require_once 'libs/FileSystem2.inc';
+
+function one() {
+ Channels::includeSystem('MySystem2');
+ $siteid = MySystem2::getCurrentSiteId();
+ Page::create();
+ PageAssetType::create();
+}
+
+function two() {
+ $siteid = MySystem2::getCurrentSiteId();
+ $parserFiles = FileSystem2::listDirectory();
+}
+
+function three() {
+ include 'libs/FileSystem.inc';
+ $siteid = MySystem::getCurrentSiteId();
+ $parserFiles = FileSystem::listDirectory();
+}
+
+$siteid = MySystem2::getCurrentSiteId();
+$parserFiles = FileSystem2::listDirectory();
+$siteid = MySystem::getCurrentSiteId();
+$parserFiles = FileSystem::listDirectory();
+Page::create();
+PageAssetType::create();
+
+class AssetListingAssetType extends AssetAssetType
+{
+ function one() {
+ Channels::includeSystem('MySystem2');
+ $siteid = MySystem2::getCurrentSiteId();
+ Page::create();
+ PageAssetType::create();
+ }
+
+ function two() {
+ $siteid = MySystem2::getCurrentSiteId();
+ $parserFiles = FileSystem2::listDirectory();
+ return parent::two();
+ }
+
+ function three() {
+ include 'libs/FileSystem.inc';
+ $siteid = MySystem::getCurrentSiteId();
+ $parserFiles = FileSystem::listDirectory();
+ echo self::two();
+ echo static::two();
+ }
+}
+
+echo Init::ROOT_DIR;
+
+Channels::includeSystem('AssetType');
+AssetType::includeAsset('User');
+AssetType::includeAsset('FolderAsset');
+UserAssetType::create();
+FolderAssetType::create();
+$query->fetch(PDO::FETCH_NUM)
+BaseSystem::getDataDir();
+Util::getArrayIndex(array(), '');
+
+
+Channels::includeSystem('Widget');
+Widget::includeWidget('AbstractContainer');
+class MyWidget extends AbstractContainerWidgetType {}
+class MyOtherWidget extends BookWidgetType {}
+
+$zip = new ZipArchive();
+$res = $zip->open($path, ZipArchive::CREATE);
+
+class AssetListingUnitTest extends AbstractMySourceUnitTest
+{
+ function setUp() {
+ parent::setUp();
+ Channels::includeSystem('MySystem2');
+ include_once 'Libs/FileSystem.inc';
+ }
+
+ function two() {
+ $siteid = MySystem2::getCurrentSiteId();
+ $parserFiles = FileSystem::listDirectory();
+ }
+
+ function three() {
+ $siteid = MySystem3::getCurrentSiteId();
+ $parserFiles = FileSystem::listDirectory();
+ }
+}
+
+if (Channels::systemExists('Log') === TRUE) {
+ Channels::includeSystem('Log');
+} else {
+ return;
+}
+
+Log::addProjectLog('metadata.field.update', $msg);
+
+function two() {
+ Widget::includeWidget('CacheAdminScreen');
+ $barChart = CacheAdminScreenWidgetType::constructBarchart($data);
+}
+
+$adjustDialog->setOrientation(AbstractWidgetWidgetType::CENTER);
+
+$className = 'SquizPerspective'.ucfirst($property['type']).'PropertyType';
+Channels::includeSystem($className);
+$className::setValue($assetid, $propertyid, $perspectives, $value, (array) $property['settings']);
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the IncludeSystem 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\Standards\MySource\Tests\Channels;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class IncludeSystemUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 9 => 1,
+ 14 => 1,
+ 24 => 1,
+ 27 => 1,
+ 28 => 1,
+ 31 => 1,
+ 36 => 1,
+ 41 => 1,
+ 61 => 1,
+ 70 => 1,
+ 89 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+Channels::includeSystem('OurSystem');
+Channels::includeSystem('Page');
+Channels::includeAsset('Page2');
+Channels::includeAsset('Folder');
+
+function one() {
+ Channels::includeSystem('MySystem');
+ $siteid = MySystem2::getCurrentSiteId();
+ Page::create();
+ PageAssetType::create();
+}
+
+function two() {
+ Page2AssetType::create();
+ Folder::getFolder();
+}
+
+Channels::includeAsset('Asset');
+class AssetListingAssetType extends AssetAssetType
+{
+ function one() {
+ Channels::includeSystem('MySystem2');
+ Channels::includeSystem('Page2');
+ $siteid = MySystem2::getCurrentSiteId();
+ }
+
+ function two() {
+ $siteid = Page2::getCurrentSiteId();
+ }
+}
+
+Channels::includeSystem('Log');
+Channels::includeSystem('Log4');
+class MyLog implements Dog, Log extends LogMe {}
+
+Channels::includeSystem('Log1');
+class MyLog implements Dog, Log1 extends LogMe {}
+
+class MyLog implements Log2 {}
+
+Channels::includeSystem('Log3');
+class MyLog implements Log3 {}
+
+if (self::systemExists($systemName) === FALSE) {
+ return $actions;
+} else {
+ // Shouldn't throw an error because we don't know the system name.
+ self::includeSystem($systemName);
+ self::includeSystem($systemNames['log']);
+}
+
+Channels::includeSystem('Widget');
+Widget::includeWidget('AbstractContainer');
+class MyOtherWidget extends BookWidgetType {}
+
+function myFunction()
+{
+ if (Channels::systemExists('Log5') === TRUE) {
+ Channels::includeSystem('Log5');
+ } else {
+ return;
+ }
+
+ Log5::addProjectLog('metadata.field.update', $msg);
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the UnusedSystem 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\Standards\MySource\Tests\Channels;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class UnusedSystemUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 5 => 1,
+ 8 => 1,
+ 24 => 1,
+ 34 => 1,
+ 54 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Short content.
+ *
+ * @return void
+ *
+ * @api read
+ * @api-privilege asset.read.content
+ * @api-mustlock false
+ */
+private function functionCall() {}
+
+/**
+ * Short content.
+ *
+ * @return void
+ *
+ * @api read
+ */
+private function functionCall() {}
+
+/**
+ * Short content.
+ *
+ * @api read
+ *
+ * @return void
+ */
+private function functionCall() {}
+
+/**
+ * Short content.
+ *
+ * @return void
+ *
+ * @api-privilege asset.read.content
+ * @api read
+ * @api-mustlock false
+ */
+private function functionCall() {}
+
+/**
+ * Short content.
+ *
+ * @return void
+ *
+ * @api read
+ *
+ * @api-privilege asset.read.content
+ * @api-mustlock false
+ */
+private function functionCall() {}
+
+/**
+ * Short content.
+ *
+ * @return void
+ * @api read
+ */
+private function functionCall() {}
+
+/**
+ * Test unknown tags.
+ *
+ * @return void
+ * @since read
+ * @hello hello
+ * @package Test
+ */
+private function functionCall() {}
+
+/**
+ * Short content.
+ *
+ * @return void
+ *
+ * @api read
+ * @api-privilege asset.read.content
+ * @api-mustlock false
+ */
+private function functionCall() {}
+
+/**
+ * Short content.
+ *
+ * @return void
+ *
+ * @api read
+ * @api-mustlock false
+ */
+private function functionCall() {}
+
+/**
+ * Short content.
+ *
+ * @return void
+ *
+ * @api read
+ */
+private function functionCall() {}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionComment 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\Standards\MySource\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 28 => 1,
+ 36 => 1,
+ 37 => 2,
+ 49 => 1,
+ 58 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+Debug::bam('test');
+Debug::minbam($variable);
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the DebugCode 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\Standards\MySource\Tests\Debug;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DebugCodeUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 3 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+console.info();
+console.warn();
+console.test();
+con.sole();
+var console = {
+ console: 'string';
+};
+function console() {}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the FirebugConsole 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\Standards\MySource\Tests\Debug;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FirebugConsoleUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='FirebugConsoleUnitTest.js')
+ {
+ if ($testFile !== 'FirebugConsoleUnitTest.js') {
+ return array();
+ }
+
+ return array(
+ 1 => 1,
+ 2 => 1,
+ 3 => 1,
+ 5 => 1,
+ 6 => 1,
+ 8 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+var self = this;
+buttonWidget.addClickEvent(function() {
+ self.addDynamicSouce();
+});
+
+var x = self;
+var y = this;
+
+var test = '';
+if (true) {
+ test = this
+}
+
+var itemid = this.items[i].getAttribute('itemid');
+
+for (var x = this; y < 10; y++) {
+ var x = this + 1;
+}
+
+var _self = this;
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the AssignThis 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\Standards\MySource\Tests\Objects;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class AssignThisUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='AssignThisUnitTest.js')
+ {
+ if ($testFile !== 'AssignThisUnitTest.js') {
+ return array();
+ }
+
+ return array(
+ 7 => 1,
+ 11 => 1,
+ 16 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+SampleWidgetType.prototype = {
+
+ create: function(callback)
+ {
+ if (x === 1) {
+ return;
+ }
+
+ if (y === 1) {
+ callback.call(this);
+ // A comment here to explain the return is okay.
+ return;
+ }
+
+ if (a === 1) {
+ // Cant return value even after calling callback.
+ callback.call(this);
+ return something;
+ }
+
+ if (a === 1) {
+ // Need to pass self or this to callback function.
+ callback.call(a);
+ }
+
+ callback.call(self);
+
+ var self = this;
+ this.createChildren(null, function() {
+ callback.call(self, div);
+ });
+
+ // Never good to return a vaue.
+ return something;
+
+ callback.call(self);
+ }
+
+};
+
+AnotherSampleWidgetType.prototype = {
+
+ create: function(input)
+ {
+ return;
+ }
+
+ getSomething: function(input)
+ {
+ return 1;
+ }
+
+};
+
+
+NoCreateWidgetType.prototype = {
+
+ getSomething: function(input)
+ {
+ return;
+ }
+
+};
+
+
+SomeRandom.prototype = {
+
+ create: function(input)
+ {
+ return;
+ }
+
+};
+
+SampleWidgetType.prototype = {
+
+ create: function(callback)
+ {
+ if (a === 1) {
+ // This is ok because it is the last statement,
+ // even though it is conditional.
+ callback.call(self);
+ }
+
+ }
+
+};
+
+SampleWidgetType.prototype = {
+
+ create: function(callback)
+ {
+ var something = callback;
+
+ }
+
+};
+
+SampleWidgetType.prototype = {
+
+ create: function(callback)
+ {
+ // Also valid because we are passing the callback to
+ // someone else to call.
+ if (y === 1) {
+ this.something(callback);
+ return;
+ }
+
+ this.init(callback);
+
+ }
+
+};
+
+SampleWidgetType.prototype = {
+
+ create: function(callback)
+ {
+ // Also valid because we are passing the callback to
+ // someone else to call.
+ if (y === 1) {
+ this.something(callback);
+ }
+
+ this.init(callback);
+
+ }
+
+};
+
+SampleWidgetType.prototype = {
+
+ create: function(callback)
+ {
+ if (a === 1) {
+ // This is ok because it is the last statement,
+ // even though it is conditional.
+ this.something(callback);
+ }
+
+ }
+
+};
+
+
+SampleWidgetType.prototype = {
+
+ create: function(callback)
+ {
+ if (dfx.isFn(callback) === true) {
+ callback.call(this, cont);
+ return;
+ }
+ }
+
+};
+
+
+SampleWidgetType.prototype = {
+
+ create: function(callback)
+ {
+ dfx.foreach(items, function(item) {
+ return true;
+ });
+
+ if (dfx.isFn(callback) === true) {
+ callback.call(this);
+ }
+ }
+
+};
+
+SampleWidgetType.prototype = {
+
+ create: function(callback)
+ {
+ var self = this;
+ this.createChildren(null, function() {
+ callback.call(self, div);
+ return;
+ });
+ }
+
+};
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the CreateWidgetTypeCallback 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\Standards\MySource\Tests\Objects;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class CreateWidgetTypeCallbackUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='CreateWidgetTypeCallbackUnitTest.js')
+ {
+ return array(
+ 18 => 1,
+ 23 => 2,
+ 26 => 1,
+ 30 => 1,
+ 34 => 1,
+ 43 => 2,
+ 91 => 1,
+ 123 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+Channels::includeSystem('Widget');
+Widget::includeWidget('PasswordField');
+$blah = new PasswordFieldWidgetType();
+Widget::getWidget('PasswordField');
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowNewWidget 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\Standards\MySource\Tests\Objects;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowNewWidgetUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(4 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Adds a new issue.
+ *
+ * Returns the new issue id.
+ *
+ * @param string $title Title of the new issue.
+ * @param string $description The description of the issue.
+ * @param string $reporter Asset id of the reporter.
+ * @param integer $projectid Id of the project that the issue belongs to.
+ * @param array $tags Array of tags.
+ * @param string $status The status of the issue.
+ * @param string $assignedTo The asset id of the user that the issue is
+ * assigned to.
+ * @param string $reportedDate If set then this date will be used instead of the
+ * current date and time.
+ * @param integer $reportedMilestone Reported milestone.
+ *
+ * @return integer
+ * @throws ChannelException If there is an error.
+ *
+ * @api write
+ * @api-permission public
+ */
+public static function addIssue(
+ $title,
+ $description,
+ $reporter=NULL,
+ $projectid=NULL,
+ array $tags=array(),
+ $status=NULL,
+ $assignedTo=NULL,
+ $reportedDate=NULL,
+ $reportedMilestone=NULL
+) {
+ // Get current projectid if not specified.
+ if ($projectid === NULL) {
+ Channels::includeSystem('Project');
+ $projectid = Project::getCurrentProjectId();
+ Channels::modifyBasket('project', $projectid);
+ }
+
+ Channels::includeSystem('SquizRoadmap');
+ Channels::includeSystem('Permission');
+ if (Permission::hasPermission($projectid, 'ideas.contribute') === FALSE) {
+ throw new ChannelException(_('You do not have permission to contribute idea'));
+ }
+
+ if ($assignedTo !== NULL) {
+ if (Permission::hasPermission($projectid, 'ideas.edit.details') === FALSE) {
+ throw new ChannelException(_('You do not have permission to assign user to idea'));
+ }
+
+ if (SquizRoadmap::isVisibleProject($projectid, $assignedTo) === FALSE) {
+ throw new ChannelException(_('Assigned to user does not have access to issue project.'));
+ }
+ }
+
+ // Get current user id if not specified.
+ if ($reporter === NULL) {
+ Channels::includeSystem('User');
+ $reporter = User::getCurrentUserid();
+ Channels::modifyBasket('reporter', $reporter);
+ }
+
+ if (SquizRoadmap::isVisibleProject($projectid, $reporter) === FALSE) {
+ throw new ChannelException(_('Contributed by user does not have access to issue project.'));
+ }
+
+ // Make sure status is valid.
+ Channels::includeSystem('SquizRoadmap');
+ Channels::includeSystem('SquizRoadmapStatus');
+ if ($status === NULL) {
+ $statuses = SquizRoadmapStatus::getStatus($projectid);
+ if (empty($statuses) === TRUE) {
+ throw new ChannelException(_('No defined statuses in project'));
+ }
+
+ $status = $statuses[0]['status'];
+ Channels::modifyBasket('status', $status);
+ } else if (SquizRoadmapStatus::isValidStatus($projectid, $status) === FALSE) {
+ throw new ChannelException(sprintf(_('Invalid status: %s'), $status));
+ }
+
+ $issueid = DAL::seqNextVal('sq_rdm_issue_seq');
+ Channels::addToBasket('issueid', $issueid);
+
+ if ($reportedDate === NULL) {
+ include_once 'Libs/String/String.inc';
+ $reportedDate = String::tsIso8601(time());
+ Channels::modifyBasket('reportedDate', $reportedDate);
+ }
+
+ $title = trim($title);
+ Channels::modifyBasket('title', $title);
+ if (empty($title) === TRUE) {
+ throw new ChannelException(_('Title cannot be empty'));
+ }
+
+ $description = SquizRoadmap::stripTags(trim($description));
+ Channels::modifyBasket('description', $description);
+ if (empty($description) === TRUE) {
+ throw new ChannelException(_('Description cannot be empty'));
+ }
+
+ try {
+ DAL::beginTransaction();
+
+ $query = DAL::getDALQuery('SquizRoadmapIssue', 'addIssue');
+ DAL::executeQuery($query);
+
+ // Add tags for the new issue.
+ SquizRoadmapIssue::addIssueTags($issueid, $tags);
+
+ // Add reporter and assignee to watch list.
+ SquizRoadmapIssue::addIssueWatch($issueid, $reporter);
+
+ if ($assignedTo !== NULL) {
+ SquizRoadmapIssue::addIssueWatch($issueid, $assignedTo);
+ }
+
+ SquizRoadmapIssue::clearIssueCache($issueid);
+
+ DAL::commit();
+ } catch (Exception $e) {
+ DAL::rollBack();
+ throw new ChannelException($e->getMessage());
+ }//end try
+
+ if ($something === NULL) {
+ if ($bar !== NULL) {
+ }
+ }
+
+ return $issueid;
+
+}//end addIssue()
+
+/**
+ * Adds a new issue.
+ *
+ * Returns the new issue id.
+ *
+ * @param string $title Title of the new issue.
+ * @param string $description The description of the issue.
+ * @param string $reporter Asset id of the reporter.
+ * @param integer $projectid Id of the project that the issue belongs to.
+ * @param array $tags Array of tags.
+ * @param string $status The status of the issue.
+ * @param string $assignedTo The asset id of the user that the issue is
+ * assigned to.
+ * @param string $reportedDate If set then this date will be used instead of the
+ * current date and time.
+ * @param integer $reportedMilestone Reported milestone.
+ *
+ * @return integer
+ * @throws ChannelException If there is an error.
+ *
+ */
+public static function addIssue(
+ $title,
+ $description,
+ $reporter=NULL,
+ $projectid=NULL,
+ array $tags=array(),
+ $status=NULL,
+ $assignedTo=NULL,
+ $reportedDate=NULL,
+ $reportedMilestone=NULL
+) {
+ // Get current projectid if not specified.
+ if ($projectid === NULL) {
+ Channels::includeSystem('Project');
+ $projectid = Project::getCurrentProjectId();
+ Channels::modifyBasket('project', $projectid);
+ }
+
+}//end addIssue()
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the AjaxNullComparison 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\Standards\MySource\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class AjaxNullComparisonUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 37 => 1,
+ 49 => 1,
+ 60 => 1,
+ 73 => 1,
+ 88 => 1,
+ 118 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+function getWidget($type)
+{
+ eval('$obj = new '.$type.'();');
+ return $obj;
+
+}//end getWidget()
+
+function getWidget2($type)
+{
+ $string = '$obj = new '.$type.'();';
+ eval($string);
+ eval('$string = "";');
+ return $obj;
+
+}//end getWidget2()
+
+function getWidget3($type)
+{
+ $string = '$obj = new ';
+ eval($string.$type.'();');
+ return $obj;
+
+}//end getWidget3()
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the EvalObjectFactory 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\Standards\MySource\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class EvalObjectFactoryUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 4 => 1,
+ 12 => 1,
+ 21 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+echo $_REQUEST['action'];
+Security::getRequestData('action');
+
+echo $_POST['action'];
+Security::getRequestData('action', 'post');
+
+echo $_GET[$action];
+Security::getRequestData($action, 'get');
+
+class Security
+{
+ function getRequestData($var) {
+ echo $_REQUEST[$var];
+ }
+}
+
+class Insecurity
+{
+ function getRequestData($var) {
+ unset($_REQUEST[$var]);
+ echo 'OMGHAX!';
+ }
+}
+
+$sample = Util::getArrayIndex($_REQUEST, 'sample', '');
+$syntax = Util::getArrayIndex($_REQUEST, 'syntax', '');
+$value = Util::getArrayIndex($_FILES, $key, $default);
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the GetRequestData 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\Standards\MySource\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class GetRequestDataUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 5 => 1,
+ 8 => 1,
+ 21 => 1,
+ 26 => 1,
+ 27 => 1,
+ 28 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+return myFunction();
+return MyClass::myFunction();
+return $obj->myFunction();
+return $obj->variable;
+return MyClass::VARIABLE;
+return $variable;
+return ($var + 1);
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ReturnFunctionValue 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\Standards\MySource\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ReturnFunctionValueUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 2 => 1,
+ 3 => 1,
+ 4 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+one = (1 + 2);
+two = (one + 2);
+two = (one + one);
+three = ('1' + 2);
+
+four = ['1', two].join();
+four = ['1', two].join('');
+four = ['1', [one, two].join(',')].join(' ');
+four = ['1', [one, two].join()].join(' ');
+four = ['1', [one, two].join()].join();
+
+five = 'string' + ['1', [one, two].join()].join() + 'string';
+
+six = myArray.join(' ');
+six = [arrayOne, arrayTwo].join();
+
+// This is fine because the array is not created inline.
+var x = 'x' + test[x].join('p') + 't';
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the JoinStrings 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\Standards\MySource\Tests\Strings;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class JoinStringsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='JoinStringsUnitTest.js')
+ {
+ if ($testFile !== 'JoinStringsUnitTest.js') {
+ return array();
+ }
+
+ return array(
+ 6 => 1,
+ 7 => 1,
+ 8 => 2,
+ 9 => 2,
+ 10 => 2,
+ 12 => 2,
+ 15 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?xml version="1.0"?>
+<ruleset name="MySource">
+ <description>The MySource coding standard builds on the Squiz coding standard. Currently used for MySource Mini development.</description>
+
+ <exclude-pattern>*/Tests/*</exclude-pattern>
+ <exclude-pattern>*/Oven/*</exclude-pattern>
+ <exclude-pattern>*/data/*</exclude-pattern>
+ <exclude-pattern>*/jquery.js</exclude-pattern>
+ <exclude-pattern>*/jquery.*.js</exclude-pattern>
+ <exclude-pattern>*/viper/*</exclude-pattern>
+ <exclude-pattern>DALConf.inc</exclude-pattern>
+
+ <!-- Include the whole Squiz standard except FunctionComment, which we override -->
+ <rule ref="Squiz">
+ <exclude name="Squiz.Commenting.FunctionComment"/>
+ </rule>
+
+</ruleset>
--- /dev/null
+<documentation title="Class Declarations">
+ <standard>
+ <![CDATA[
+ The opening brace of a class must be on the line after the definition by itself.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Opening brace on the correct line.">
+ <![CDATA[
+class Foo
+<em>{</em>
+}
+ ]]>
+ </code>
+ <code title="Invalid: Opening brace on same line as declaration.">
+ <![CDATA[
+class Foo <em>{</em>
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Class Comments">
+ <standard>
+ <![CDATA[
+ Classes and interfaces must have a non-empty doc comment. The short description must be on the second line of the comment. Each description must have one blank comment line before and after. There must be one blank line before the tags in the comments. A @version tag must be in Release: package_version format.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A doc comment for the class.">
+ <![CDATA[
+<em>/**
+ * The Foo class.
+ */</em>
+class Foo
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: No doc comment for the class.">
+ <![CDATA[
+class Foo
+{
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: A doc comment for the class.">
+ <![CDATA[
+<em>/**
+ * The Foo class.
+ */</em>
+class Foo
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: Invalid comment type for the class.">
+ <![CDATA[
+// The Foo class.
+class Foo
+{
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: A doc comment for the class.">
+ <![CDATA[
+<em>/**
+ * The Foo class.
+ */</em>
+class Foo
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: The blank line after the comment makes it appear as a file comment, not a class comment.">
+ <![CDATA[
+<em>/**
+ * The Foo class.
+ */</em>
+
+class Foo
+{
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Short description is the second line of the comment.">
+ <![CDATA[
+/**
+ * <em>The Foo class.</em>
+ */
+class Foo
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: An extra blank line before the short description.">
+ <![CDATA[
+/**
+ *
+ * <em>The Foo class.</em>
+ */
+class Foo
+{
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Exactly one blank line around descriptions.">
+ <![CDATA[
+/**
+ * The Foo class.
+ * <em></em>
+ * A helper for the Bar class.
+ * <em></em>
+ * @see Bar
+ */
+class Foo
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: Extra blank lines around the descriptions.">
+ <![CDATA[
+/**
+ * The Foo class.
+ * <em></em>
+ * <em></em>
+ * A helper for the Bar class.
+ * <em></em>
+ * <em></em>
+ * @see Bar
+ */
+class Foo
+{
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Exactly one blank line before the tags.">
+ <![CDATA[
+/**
+ * The Foo class.
+ * <em></em>
+ * @see Bar
+ */
+class Foo
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: Extra blank lines before the tags.">
+ <![CDATA[
+/**
+ * The Foo class.
+ * <em></em>
+ * <em></em>
+ * @see Bar
+ */
+class Foo
+{
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Version tag is in the correct format.">
+ <![CDATA[
+/**
+ * The Foo class.
+ *
+ * @version <em>Release: 1.0</em>
+ */
+class Foo
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: No Release: text.">
+ <![CDATA[
+/**
+ * The Foo class.
+ *
+ * @version <em>1.0</em>
+ */
+class Foo
+{
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="File Comments">
+ <standard>
+ <![CDATA[
+ Files must have a non-empty doc comment. The short description must be on the second line of the comment. Each description must have one blank comment line before and after. There must be one blank line before the tags in the comments. There must be a category, package, author, license, and link tag. There may only be one category, package, subpackage, license, version, since and deprecated tag. The tags must be in the order category, package, subpackage, author, copyright, license, version, link, see, since, and deprecated. The php version must be specified.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A file comment is used.">
+ <![CDATA[
+<?php
+<em>/**
+ * Short description here.
+ *
+ * PHP version 5
+ *
+ * @category Foo
+ * @package Foo_Helpers
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */</em>
+ ]]>
+ </code>
+ <code title="Invalid: No doc comment for the class.">
+ <![CDATA[
+<?php
+<em></em>
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Short description is the second line of the comment.">
+ <![CDATA[
+<?php
+/**
+ * <em>Short description here.</em>
+ *
+ * PHP version 5
+ *
+ * @category Foo
+ * @package Foo_Helpers
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ <code title="Invalid: An extra blank line before the short description.">
+ <![CDATA[
+<?php
+/**
+ * <em></em>
+ * <em>Short description here.</em>
+ *
+ * PHP version 5
+ *
+ * @category Foo
+ * @package Foo_Helpers
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Exactly one blank line around descriptions.">
+ <![CDATA[
+<?php
+/**
+ * Short description here.
+ * <em></em>
+ * PHP version 5
+ * <em></em>
+ * @category Foo
+ * @package Foo_Helpers
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ <code title="Invalid: Extra blank lines around the descriptions.">
+ <![CDATA[
+<?php
+/**
+ * Short description here.
+ * <em></em>
+ * <em></em>
+ * PHP version 5
+ * <em></em>
+ * <em></em>
+ * @category Foo
+ * @package Foo_Helpers
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Exactly one blank line before the tags.">
+ <![CDATA[
+<?php
+/**
+ * Short description here.
+ *
+ * PHP version 5
+ * <em></em>
+ * @category Foo
+ * @package Foo_Helpers
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ <code title="Invalid: Extra blank lines before the tags.">
+ <![CDATA[
+<?php
+/**
+ * Short description here.
+ *
+ * PHP version 5
+ * <em></em>
+ * <em></em>
+ * @category Foo
+ * @package Foo_Helpers
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: All required tags are used.">
+ <![CDATA[
+<?php
+/**
+ * Short description here.
+ *
+ * PHP version 5
+ *
+ * <em>@category</em> Foo
+ * <em>@package</em> Foo_Helpers
+ * <em>@author</em> Marty McFly <mmcfly@example.com>
+ * <em>@copyright</em> 2013-2014 Foo Inc.
+ * <em>@license</em> MIT License
+ * <em>@link</em> http://example.com
+ */
+ ]]>
+ </code>
+ <code title="Invalid: Missing an author tag.">
+ <![CDATA[
+<?php
+/**
+ * Short description here.
+ *
+ * PHP version 5
+ *
+ * @category Foo
+ * @package Foo_Helpers
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Tags that should only be used once are only used once.">
+ <![CDATA[
+<?php
+/**
+ * Short description here.
+ *
+ * PHP version 5
+ *
+ * <em>@category</em> Foo
+ * <em>@package</em> Foo_Helpers
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * <em>@license</em> MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ <code title="Invalid: Multiple category tags.">
+ <![CDATA[
+<?php
+/**
+ * Short description here.
+ *
+ * PHP version 5
+ *
+ * <em>@category</em> Foo
+ * <em>@category</em> Bar
+ * @package Foo_Helpers
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: PHP version specified.">
+ <![CDATA[
+<?php
+/**
+ * Short description here.
+ *
+ * <em>PHP version 5</em>
+ *
+ * @category Foo
+ * @package Foo_Helpers
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ <code title="Invalid: Category and package tags are swapped in order.">
+ <![CDATA[
+<?php
+/**
+ * Short description here.
+ *
+ * PHP version 5
+ *
+ * <em>@package</em> Foo_Helpers
+ * <em>@category</em> Foo
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Tags are in the correct order.">
+ <![CDATA[
+<?php
+/**
+ * Short description here.
+ *
+ * PHP version 5
+ *
+ * @category Foo
+ * @package Foo_Helpers
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ <code title="Invalid: No php version specified.">
+ <![CDATA[
+<?php
+/**
+ * Short description here.
+ *
+ * @category Foo
+ * @package Foo_Helpers
+ * @author Marty McFly <mmcfly@example.com>
+ * @copyright 2013-2014 Foo Inc.
+ * @license MIT License
+ * @link http://example.com
+ */
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Function Comments">
+ <standard>
+ <![CDATA[
+ Functions must have a non-empty doc comment. The short description must be on the second line of the comment. Each description must have one blank comment line before and after. There must be one blank line before the tags in the comments. There must be a tag for each of the parameters in the right order with the right variable names with a comment. There must be a return tag. Any throw tag must have an exception class.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A function doc comment is used.">
+ <![CDATA[
+<em>/**
+ * Short description here.
+ *
+ * @return void
+ */</em>
+ function foo()
+ {
+ }
+ ]]>
+ </code>
+ <code title="Invalid: No doc comment for the function.">
+ <![CDATA[
+ function foo()
+ {
+ }
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Short description is the second line of the comment.">
+ <![CDATA[
+/**
+ * <em>Short description here.</em>
+ *
+ * @return void
+ */
+ function foo()
+ {
+ }
+ ]]>
+ </code>
+ <code title="Invalid: An extra blank line before the short description.">
+ <![CDATA[
+/**
+ * <em></em>
+ * <em>Short description here.</em>
+ *
+ * @return void
+ */
+ function foo()
+ {
+ }
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Exactly one blank line around descriptions.">
+ <![CDATA[
+/**
+ * Short description here.
+ * <em></em>
+ * Long description here.
+ * <em></em>
+ * @return void
+ */
+ function foo()
+ {
+ }
+ ]]>
+ </code>
+ <code title="Invalid: Extra blank lines around the descriptions.">
+ <![CDATA[
+/**
+ * Short description here.
+ * <em></em>
+ * <em></em>
+ * Long description here.
+ * <em></em>
+ * <em></em>
+ * @return void
+ */
+ function foo()
+ {
+ }
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Exactly one blank line before the tags.">
+ <![CDATA[
+/**
+ * Short description here.
+ *
+ * Long description here.
+ * <em></em>
+ * @return void
+ */
+ function foo()
+ {
+ }
+ ]]>
+ </code>
+ <code title="Invalid: Extra blank lines before the tags.">
+ <![CDATA[
+/**
+ * Short description here.
+ *
+ * Long description here.
+ * <em></em>
+ * <em></em>
+ * @return void
+ */
+ function foo()
+ {
+ }
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Throws tag has an exception class.">
+ <![CDATA[
+/**
+ * Short description here.
+ *
+ * @return void
+ * @throws <em>FooException</em>
+ */
+ function foo()
+ {
+ }
+ ]]>
+ </code>
+ <code title="Invalid: No exception class given for throws tag.">
+ <![CDATA[
+/**
+ * Short description here.
+ *
+ * @return void
+ * <em>@throws</em>
+ */
+ function foo()
+ {
+ }
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Return tag present.">
+ <![CDATA[
+/**
+ * Short description here.
+ *
+ * <em>@return void</em>
+ */
+ function foo()
+ {
+ }
+ ]]>
+ </code>
+ <code title="Invalid: No return tag.">
+ <![CDATA[
+/**
+ * Short description here.
+ */
+ function foo()
+ {
+ }
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Param names are correct.">
+ <![CDATA[
+/**
+ * Short description here.
+ *
+ * @param string <em>$foo</em> Foo parameter
+ * @param string <em>$bar</em> Bar parameter
+ * @return void
+ */
+ function foo(<em>$foo</em>, <em>$bar</em>)
+ {
+ }
+ ]]>
+ </code>
+ <code title="Invalid: Wrong parameter name doesn't match function signature.">
+ <![CDATA[
+/**
+ * Short description here.
+ *
+ * @param string $foo Foo parameter
+ * @param string <em>$qux</em> Bar parameter
+ * @return void
+ */
+ function foo($foo, <em>$bar</em>)
+ {
+ }
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Param names are ordered correctly.">
+ <![CDATA[
+/**
+ * Short description here.
+ *
+ * @param string <em>$foo</em> Foo parameter
+ * @param string <em>$bar</em> Bar parameter
+ * @return void
+ */
+ function foo(<em>$foo</em>, <em>$bar</em>)
+ {
+ }
+ ]]>
+ </code>
+ <code title="Invalid: Wrong parameter order.">
+ <![CDATA[
+/**
+ * Short description here.
+ *
+ * @param string <em>$bar</em> Bar parameter
+ * @param string <em>$foo</em> Foo parameter
+ * @return void
+ */
+ function foo(<em>$foo</em>, <em>$bar</em>)
+ {
+ }
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Inline Comments">
+ <standard>
+ <![CDATA[
+ Perl-style # comments are not allowed.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A // style comment.">
+ <![CDATA[
+<em>//</em> A comment.
+ ]]>
+ </code>
+ <code title="Invalid: A # style comment.">
+ <![CDATA[
+<em>#</em> A comment.
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Control Structure Signatures">
+ <standard>
+ <![CDATA[
+ Control structures should use one space around the parentheses in conditions. The opening brace should be preceded by one space and should be at the end of the line.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Correct spacing around the condition.">
+ <![CDATA[
+if<em> </em>($foo)<em> </em>{
+}
+ ]]>
+ </code>
+ <code title="Invalid: Incorrect spacing around the condition.">
+ <![CDATA[
+if<em></em>($foo)<em></em>{
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Correct placement of the opening brace.">
+ <![CDATA[
+if ($foo) <em>{</em>
+}
+ ]]>
+ </code>
+ <code title="Invalid: Incorrect placement of the opening brace on a new line.">
+ <![CDATA[
+if ($foo)
+<em>{</em>
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Multi-line If Conditions">
+ <standard>
+ <![CDATA[
+ Multi-line if conditions should be indented one level and each line should begin with a boolean operator. The end parenthesis should be on a new line.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Correct indentation.">
+ <![CDATA[
+if ($foo
+<em> </em>&& $bar
+) {
+}
+ ]]>
+ </code>
+ <code title="Invalid: No indentation used on the condition lines.">
+ <![CDATA[
+if ($foo
+<em></em>&& $bar
+) {
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Boolean operator at the start of the line.">
+ <![CDATA[
+if ($foo
+ <em>&&</em> $bar
+) {
+}
+ ]]>
+ </code>
+ <code title="Invalid: Boolean operator at the end of the line.">
+ <![CDATA[
+if ($foo <em>&&</em>
+ $bar
+) {
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: End parenthesis on a new line.">
+ <![CDATA[
+if ($foo
+ && $bar
+<em>)</em> {
+}
+ ]]>
+ </code>
+ <code title="Invalid: End parenthesis not moved to a new line.">
+ <![CDATA[
+if ($foo
+ && $bar<em>)</em> {
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Including Code">
+ <standard>
+ <![CDATA[
+ Anywhere you are unconditionally including a class file, use <em>require_once</em>. Anywhere you are conditionally including a class file (for example, factory methods), use <em>include_once</em>. Either of these will ensure that class files are included only once. They share the same file list, so you don't need to worry about mixing them - a file included with <em>require_once</em> will not be included again by <em>include_once</em>.
+ ]]>
+ </standard>
+ <standard>
+ <![CDATA[
+ Note that <em>include_once</em> and <em>require_once</em> are statements, not functions. Parentheses should not surround the subject filename.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: used as statement">
+ <![CDATA[
+require_once 'PHP/CodeSniffer.php';
+ ]]>
+ </code>
+ <code title="Invalid: used as function">
+ <![CDATA[
+require_once<em>(</em>'PHP/CodeSniffer.php'<em>)</em>;
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Line Length">
+ <standard>
+ <![CDATA[
+ It is recommended to keep lines at approximately 85 characters long for better code readability.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="Multi-Line Assignment">
+ <standard>
+ <![CDATA[
+ Multi-line assignment should have the equals sign be the first item on the second line indented correctly.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Assignment operator at the start of the second line.">
+ <![CDATA[
+$foo
+ <em>=</em> $bar;
+ ]]>
+ </code>
+ <code title="Invalid: Assignment operator at end of first line.">
+ <![CDATA[
+$foo <em>=</em>
+ $bar;
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Assignment operator indented one level.">
+ <![CDATA[
+$foo
+<em> </em>= $bar;
+ ]]>
+ </code>
+ <code title="Invalid: Assignment operator not indented.">
+ <![CDATA[
+$foo
+<em></em>= $bar;
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Function Calls">
+ <standard>
+ <![CDATA[
+ Functions should be called with no spaces between the function name, the opening parenthesis, and the first parameter; and no space between the last parameter, the closing parenthesis, and the semicolon.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: spaces between parameters">
+ <![CDATA[
+$var = foo($bar, $baz, $quux);
+ ]]>
+ </code>
+ <code title="Invalid: additional spaces used">
+ <![CDATA[
+$var = foo<em> </em>(<em> </em>$bar, $baz, $quux<em> </em>)<em> </em>;
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Function Declarations">
+ <standard>
+ <![CDATA[
+ There should be exactly 1 space after the function keyword and 1 space on each side of the use keyword. Closures should use the Kernighan/Ritchie Brace style and other single-line functions should use the BSD/Allman style. Multi-line function declarations should have the parameter lists indented one level with the closing parenthesis on a newline followed by a single space and the opening brace of the function.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Correct spacing around function and use keywords.">
+ <![CDATA[
+$foo = function<em> </em>()<em> </em>use<em> </em>($bar)<em> </em>{
+};
+ ]]>
+ </code>
+ <code title="Invalid: No spacing around function and use keywords.">
+ <![CDATA[
+$foo = function<em></em>()<em></em>use<em></em>($bar)<em></em>{
+};
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Multi-line function declaration formatted properly.">
+ <![CDATA[
+function foo(
+<em> </em>$bar,
+<em> </em>$baz
+<em>) {</em>
+};
+ ]]>
+ </code>
+ <code title="Invalid: Invalid indentation and formatting of closing parenthesis.">
+ <![CDATA[
+function foo(
+<em></em>$bar,
+<em></em>$baz<em>)</em>
+<em>{</em>
+};
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Default Values in Function Declarations">
+ <standard>
+ <![CDATA[
+ Arguments with default values go at the end of the argument list.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: argument with default value at end of declaration">
+ <![CDATA[
+function connect($dsn, <em>$persistent = false</em>)
+{
+ ...
+}
+ ]]>
+ </code>
+ <code title="Invalid: argument with default value at start of declaration">
+ <![CDATA[
+function connect(<em>$persistent = false</em>, $dsn)
+{
+ ...
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Class Names">
+ <standard>
+ <![CDATA[
+ Classes should be given descriptive names. Avoid using abbreviations where possible. Class names should always begin with an uppercase letter. The PEAR class hierarchy is also reflected in the class name, each level of the hierarchy separated with a single underscore.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Examples of valid class names">
+ <![CDATA[
+Log
+Net_Finger
+HTML_Upload_Error
+ ]]>
+ </code>
+ <code title="Examples of invalid class names">
+ <![CDATA[
+log
+NetFinger
+HTML-Upload-Error
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Function and Method Names">
+ <standard>
+ <![CDATA[
+ Functions and methods should be named using the "studly caps" style (also referred to as "bumpy case" or "camel caps"). Functions should in addition have the package name as a prefix, to avoid name collisions between packages. The initial letter of the name (after the prefix) is lowercase, and each letter that starts a new "word" is capitalized.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Examples of valid function names">
+ <![CDATA[
+connect()
+getData()
+buildSomeWidget()
+XML_RPC_serializeData()
+ ]]>
+ </code>
+ <code title="Examples of invalid function names">
+ <![CDATA[
+Connect()
+get_data()
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Variable Names">
+ <standard>
+ <![CDATA[
+ Private member variable names should be prefixed with an underscore and public/protected variable names should not.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Proper member variable names.">
+ <![CDATA[
+class Foo
+{
+ public $<em>publicVar</em>;
+ protected $<em>protectedVar</em>;
+ private $<em>_privateVar</em>;
+}
+ ]]>
+ </code>
+ <code title="Invalid: underscores used on public/protected variables and not used on private variables.">
+ <![CDATA[
+class Foo
+{
+ public $<em>_publicVar</em>;
+ protected $<em>_protectedVar</em>;
+ private $<em>privateVar</em>;
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Object Operator Indentation">
+ <standard>
+ <![CDATA[
+ Chained object operators when spread out over multiple lines should be the first thing on the line and be indented by 1 level.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Object operator at the start of a new line.">
+ <![CDATA[
+$foo
+ <em>-></em>bar()
+ <em>-></em>baz();
+ ]]>
+ </code>
+ <code title="Invalid: Object operator at the end of the line.">
+ <![CDATA[
+$foo<em>-></em>
+ bar()<em>-></em>
+ baz();
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Object operator indented correctly.">
+ <![CDATA[
+$foo
+<em> </em>->bar()
+<em> </em>->baz();
+ ]]>
+ </code>
+ <code title="Invalid: Object operator not indented correctly.">
+ <![CDATA[
+$foo
+<em></em>->bar()
+<em></em>->baz();
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Closing Brace Indentation">
+ <standard>
+ <![CDATA[
+ Closing braces should be indented at the same level as the beginning of the scope.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Consistent indentation level for scope.">
+ <![CDATA[
+if ($test) {
+ $var = 1;
+}
+ ]]>
+ </code>
+ <code title="Invalid: The ending brace is indented further than the if statement.">
+ <![CDATA[
+if ($test) {
+ $var = 1;
+<em> </em>}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Scope Indentation">
+ <standard>
+ <![CDATA[
+ Any scope openers except for switch statements should be indented 1 level. This includes classes, functions, and control structures.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Consistent indentation level for scope.">
+ <![CDATA[
+function foo()
+{
+<em> </em>if ($test) {
+<em> </em>$var = 1;
+<em> </em>}
+}
+ ]]>
+ </code>
+ <code title="Invalid: Indentation is not used for scope.">
+ <![CDATA[
+function foo()
+{
+<em></em>if ($test) {
+<em></em>$var = 1;
+<em></em>}
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<?php
+/**
+ * Checks the declaration of the class is correct.
+ *
+ * @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\Standards\PEAR\Sniffs\Classes;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ClassDeclarationSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_CLASS,
+ T_INTERFACE,
+ T_TRAIT,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param integer $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $errorData = array(strtolower($tokens[$stackPtr]['content']));
+
+ if (isset($tokens[$stackPtr]['scope_opener']) === false) {
+ $error = 'Possible parse error: %s missing opening or closing brace';
+ $phpcsFile->addWarning($error, $stackPtr, 'MissingBrace', $errorData);
+ return;
+ }
+
+ $curlyBrace = $tokens[$stackPtr]['scope_opener'];
+ $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($curlyBrace - 1), $stackPtr, true);
+ $classLine = $tokens[$lastContent]['line'];
+ $braceLine = $tokens[$curlyBrace]['line'];
+ if ($braceLine === $classLine) {
+ $phpcsFile->recordMetric($stackPtr, 'Class opening brace placement', 'same line');
+ $error = 'Opening brace of a %s must be on the line after the definition';
+ $fix = $phpcsFile->addFixableError($error, $curlyBrace, 'OpenBraceNewLine', $errorData);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ if ($tokens[($curlyBrace - 1)]['code'] === T_WHITESPACE) {
+ $phpcsFile->fixer->replaceToken(($curlyBrace - 1), '');
+ }
+
+ $phpcsFile->fixer->addNewlineBefore($curlyBrace);
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ return;
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Class opening brace placement', 'new line');
+
+ if ($braceLine > ($classLine + 1)) {
+ $error = 'Opening brace of a %s must be on the line following the %s declaration; found %s line(s)';
+ $data = array(
+ $tokens[$stackPtr]['content'],
+ $tokens[$stackPtr]['content'],
+ ($braceLine - $classLine - 1),
+ );
+ $fix = $phpcsFile->addFixableError($error, $curlyBrace, 'OpenBraceWrongLine', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($curlyBrace - 1); $i > $lastContent; $i--) {
+ if ($tokens[$i]['line'] === ($tokens[$curlyBrace]['line'] + 1)) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ return;
+ }//end if
+ }//end if
+
+ if ($tokens[($curlyBrace + 1)]['content'] !== $phpcsFile->eolChar) {
+ $error = 'Opening %s brace must be on a line by itself';
+ $fix = $phpcsFile->addFixableError($error, $curlyBrace, 'OpenBraceNotAlone', $errorData);
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewline($curlyBrace);
+ }
+ }
+
+ if ($tokens[($curlyBrace - 1)]['code'] === T_WHITESPACE) {
+ $prevContent = $tokens[($curlyBrace - 1)]['content'];
+ if ($prevContent === $phpcsFile->eolChar) {
+ $spaces = 0;
+ } else {
+ $blankSpace = substr($prevContent, strpos($prevContent, $phpcsFile->eolChar));
+ $spaces = strlen($blankSpace);
+ }
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $stackPtr, true);
+ $expected = ($tokens[$first]['column'] - 1);
+ if ($spaces !== $expected) {
+ $error = 'Expected %s spaces before opening brace; %s found';
+ $data = array(
+ $expected,
+ $spaces,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $curlyBrace, 'SpaceBeforeBrace', $data);
+ if ($fix === true) {
+ $indent = str_repeat(' ', $expected);
+ if ($spaces === 0) {
+ $phpcsFile->fixer->addContentBefore($curlyBrace, $indent);
+ } else {
+ $phpcsFile->fixer->replaceToken(($curlyBrace - 1), $indent);
+ }
+ }
+ }
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Parses and verifies the doc comments for classes.
+ *
+ * @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\Standards\PEAR\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ClassCommentSniff extends FileCommentSniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_CLASS,
+ T_INTERFACE,
+ T_TRAIT,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $this->currentFile = $phpcsFile;
+
+ $tokens = $phpcsFile->getTokens();
+ $type = strtolower($tokens[$stackPtr]['content']);
+ $errorData = array($type);
+
+ $find = Tokens::$methodPrefixes;
+ $find[] = T_WHITESPACE;
+
+ $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
+ if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
+ && $tokens[$commentEnd]['code'] !== T_COMMENT
+ ) {
+ $phpcsFile->addError('Missing class doc comment', $stackPtr, 'Missing');
+ $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'no');
+ return;
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'yes');
+
+ if ($tokens[$commentEnd]['code'] === T_COMMENT) {
+ $phpcsFile->addError('You must use "/**" style comments for a class comment', $stackPtr, 'WrongStyle');
+ return;
+ }
+
+ // Check each tag.
+ $this->processTags($phpcsFile, $stackPtr, $tokens[$commentEnd]['comment_opener']);
+
+ }//end process()
+
+
+ /**
+ * Process the version tag.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $tags The tokens for these tags.
+ *
+ * @return void
+ */
+ protected function processVersion($phpcsFile, array $tags)
+ {
+ $tokens = $phpcsFile->getTokens();
+ foreach ($tags as $tag) {
+ if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
+ // No content.
+ continue;
+ }
+
+ $content = $tokens[($tag + 2)]['content'];
+ if ((strstr($content, 'Release:') === false)) {
+ $error = 'Invalid version "%s" in doc comment; consider "Release: <package_version>" instead';
+ $data = array($content);
+ $phpcsFile->addWarning($error, $tag, 'InvalidVersion', $data);
+ }
+ }
+
+ }//end processVersion()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Parses and verifies the doc comments for files.
+ *
+ * @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\Standards\PEAR\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Common;
+
+class FileCommentSniff implements Sniff
+{
+
+ /**
+ * Tags in correct order and related info.
+ *
+ * @var array
+ */
+ protected $tags = array(
+ '@category' => array(
+ 'required' => true,
+ 'allow_multiple' => false,
+ ),
+ '@package' => array(
+ 'required' => true,
+ 'allow_multiple' => false,
+ ),
+ '@subpackage' => array(
+ 'required' => false,
+ 'allow_multiple' => false,
+ ),
+ '@author' => array(
+ 'required' => true,
+ 'allow_multiple' => true,
+ ),
+ '@copyright' => array(
+ 'required' => false,
+ 'allow_multiple' => true,
+ ),
+ '@license' => array(
+ 'required' => true,
+ 'allow_multiple' => false,
+ ),
+ '@version' => array(
+ 'required' => false,
+ 'allow_multiple' => false,
+ ),
+ '@link' => array(
+ 'required' => true,
+ 'allow_multiple' => true,
+ ),
+ '@see' => array(
+ 'required' => false,
+ 'allow_multiple' => true,
+ ),
+ '@since' => array(
+ 'required' => false,
+ 'allow_multiple' => false,
+ ),
+ '@deprecated' => array(
+ 'required' => false,
+ 'allow_multiple' => false,
+ ),
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return int
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Find the next non whitespace token.
+ $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+
+ // Allow declare() statements at the top of the file.
+ if ($tokens[$commentStart]['code'] === T_DECLARE) {
+ $semicolon = $phpcsFile->findNext(T_SEMICOLON, ($commentStart + 1));
+ $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($semicolon + 1), null, true);
+ }
+
+ // Ignore vim header.
+ if ($tokens[$commentStart]['code'] === T_COMMENT) {
+ if (strstr($tokens[$commentStart]['content'], 'vim:') !== false) {
+ $commentStart = $phpcsFile->findNext(
+ T_WHITESPACE,
+ ($commentStart + 1),
+ null,
+ true
+ );
+ }
+ }
+
+ $errorToken = ($stackPtr + 1);
+ if (isset($tokens[$errorToken]) === false) {
+ $errorToken--;
+ }
+
+ if ($tokens[$commentStart]['code'] === T_CLOSE_TAG) {
+ // We are only interested if this is the first open tag.
+ return ($phpcsFile->numTokens + 1);
+ } else if ($tokens[$commentStart]['code'] === T_COMMENT) {
+ $error = 'You must use "/**" style comments for a file comment';
+ $phpcsFile->addError($error, $errorToken, 'WrongStyle');
+ $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes');
+ return ($phpcsFile->numTokens + 1);
+ } else if ($commentStart === false
+ || $tokens[$commentStart]['code'] !== T_DOC_COMMENT_OPEN_TAG
+ ) {
+ $phpcsFile->addError('Missing file doc comment', $errorToken, 'Missing');
+ $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'no');
+ return ($phpcsFile->numTokens + 1);
+ }
+
+ $commentEnd = $tokens[$commentStart]['comment_closer'];
+
+ $nextToken = $phpcsFile->findNext(
+ T_WHITESPACE,
+ ($commentEnd + 1),
+ null,
+ true
+ );
+
+ $ignore = array(
+ T_CLASS,
+ T_INTERFACE,
+ T_TRAIT,
+ T_FUNCTION,
+ T_CLOSURE,
+ T_PUBLIC,
+ T_PRIVATE,
+ T_PROTECTED,
+ T_FINAL,
+ T_STATIC,
+ T_ABSTRACT,
+ T_CONST,
+ T_PROPERTY,
+ );
+
+ if (in_array($tokens[$nextToken]['code'], $ignore) === true) {
+ $phpcsFile->addError('Missing file doc comment', $stackPtr, 'Missing');
+ $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'no');
+ return ($phpcsFile->numTokens + 1);
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes');
+
+ // Check the PHP Version, which should be in some text before the first tag.
+ $found = false;
+ for ($i = ($commentStart + 1); $i < $commentEnd; $i++) {
+ if ($tokens[$i]['code'] === T_DOC_COMMENT_TAG) {
+ break;
+ } else if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING
+ && strstr(strtolower($tokens[$i]['content']), 'php version') !== false
+ ) {
+ $found = true;
+ break;
+ }
+ }
+
+ if ($found === false) {
+ $error = 'PHP version not specified';
+ $phpcsFile->addWarning($error, $commentEnd, 'MissingVersion');
+ }
+
+ // Check each tag.
+ $this->processTags($phpcsFile, $stackPtr, $commentStart);
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+ /**
+ * Processes each required or optional tag.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $commentStart Position in the stack where the comment started.
+ *
+ * @return void
+ */
+ protected function processTags($phpcsFile, $stackPtr, $commentStart)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if (get_class($this) === 'PHP_CodeSniffer\Standards\PEAR\Sniffs\Commenting\FileCommentSniff') {
+ $docBlock = 'file';
+ } else {
+ $docBlock = 'class';
+ }
+
+ $commentEnd = $tokens[$commentStart]['comment_closer'];
+
+ $foundTags = array();
+ $tagTokens = array();
+ foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
+ $name = $tokens[$tag]['content'];
+ if (isset($this->tags[$name]) === false) {
+ continue;
+ }
+
+ if ($this->tags[$name]['allow_multiple'] === false && isset($tagTokens[$name]) === true) {
+ $error = 'Only one %s tag is allowed in a %s comment';
+ $data = array(
+ $name,
+ $docBlock,
+ );
+ $phpcsFile->addError($error, $tag, 'Duplicate'.ucfirst(substr($name, 1)).'Tag', $data);
+ }
+
+ $foundTags[] = $name;
+ $tagTokens[$name][] = $tag;
+
+ $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd);
+ if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) {
+ $error = 'Content missing for %s tag in %s comment';
+ $data = array(
+ $name,
+ $docBlock,
+ );
+ $phpcsFile->addError($error, $tag, 'Empty'.ucfirst(substr($name, 1)).'Tag', $data);
+ continue;
+ }
+ }//end foreach
+
+ // Check if the tags are in the correct position.
+ $pos = 0;
+ foreach ($this->tags as $tag => $tagData) {
+ if (isset($tagTokens[$tag]) === false) {
+ if ($tagData['required'] === true) {
+ $error = 'Missing %s tag in %s comment';
+ $data = array(
+ $tag,
+ $docBlock,
+ );
+ $phpcsFile->addError($error, $commentEnd, 'Missing'.ucfirst(substr($tag, 1)).'Tag', $data);
+ }
+
+ continue;
+ } else {
+ $method = 'process'.substr($tag, 1);
+ if (method_exists($this, $method) === true) {
+ // Process each tag if a method is defined.
+ call_user_func(array($this, $method), $phpcsFile, $tagTokens[$tag]);
+ }
+ }
+
+ if (isset($foundTags[$pos]) === false) {
+ break;
+ }
+
+ if ($foundTags[$pos] !== $tag) {
+ $error = 'The tag in position %s should be the %s tag';
+ $data = array(
+ ($pos + 1),
+ $tag,
+ );
+ $phpcsFile->addError($error, $tokens[$commentStart]['comment_tags'][$pos], ucfirst(substr($tag, 1)).'TagOrder', $data);
+ }
+
+ // Account for multiple tags.
+ $pos++;
+ while (isset($foundTags[$pos]) === true && $foundTags[$pos] === $tag) {
+ $pos++;
+ }
+ }//end foreach
+
+ }//end processTags()
+
+
+ /**
+ * Process the category tag.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $tags The tokens for these tags.
+ *
+ * @return void
+ */
+ protected function processCategory($phpcsFile, array $tags)
+ {
+ $tokens = $phpcsFile->getTokens();
+ foreach ($tags as $tag) {
+ if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
+ // No content.
+ continue;
+ }
+
+ $content = $tokens[($tag + 2)]['content'];
+ if (Common::isUnderscoreName($content) !== true) {
+ $newContent = str_replace(' ', '_', $content);
+ $nameBits = explode('_', $newContent);
+ $firstBit = array_shift($nameBits);
+ $newName = ucfirst($firstBit).'_';
+ foreach ($nameBits as $bit) {
+ if ($bit !== '') {
+ $newName .= ucfirst($bit).'_';
+ }
+ }
+
+ $error = 'Category name "%s" is not valid; consider "%s" instead';
+ $validName = trim($newName, '_');
+ $data = array(
+ $content,
+ $validName,
+ );
+ $phpcsFile->addError($error, $tag, 'InvalidCategory', $data);
+ }
+ }//end foreach
+
+ }//end processCategory()
+
+
+ /**
+ * Process the package tag.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $tags The tokens for these tags.
+ *
+ * @return void
+ */
+ protected function processPackage($phpcsFile, array $tags)
+ {
+ $tokens = $phpcsFile->getTokens();
+ foreach ($tags as $tag) {
+ if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
+ // No content.
+ continue;
+ }
+
+ $content = $tokens[($tag + 2)]['content'];
+ if (Common::isUnderscoreName($content) === true) {
+ continue;
+ }
+
+ $newContent = str_replace(' ', '_', $content);
+ $newContent = trim($newContent, '_');
+ $newContent = preg_replace('/[^A-Za-z_]/', '', $newContent);
+
+ if ($newContent === '') {
+ $error = 'Package name "%s" is not valid';
+ $data = array($content);
+ $phpcsFile->addError($error, $tag, 'InvalidPackageValue', $data);
+ } else {
+ $nameBits = explode('_', $newContent);
+ $firstBit = array_shift($nameBits);
+ $newName = strtoupper($firstBit{0}).substr($firstBit, 1).'_';
+ foreach ($nameBits as $bit) {
+ if ($bit !== '') {
+ $newName .= strtoupper($bit{0}).substr($bit, 1).'_';
+ }
+ }
+
+ $error = 'Package name "%s" is not valid; consider "%s" instead';
+ $validName = trim($newName, '_');
+ $data = array(
+ $content,
+ $validName,
+ );
+ $phpcsFile->addError($error, $tag, 'InvalidPackage', $data);
+ }//end if
+ }//end foreach
+
+ }//end processPackage()
+
+
+ /**
+ * Process the subpackage tag.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $tags The tokens for these tags.
+ *
+ * @return void
+ */
+ protected function processSubpackage($phpcsFile, array $tags)
+ {
+ $tokens = $phpcsFile->getTokens();
+ foreach ($tags as $tag) {
+ if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
+ // No content.
+ continue;
+ }
+
+ $content = $tokens[($tag + 2)]['content'];
+ if (Common::isUnderscoreName($content) === true) {
+ continue;
+ }
+
+ $newContent = str_replace(' ', '_', $content);
+ $nameBits = explode('_', $newContent);
+ $firstBit = array_shift($nameBits);
+ $newName = strtoupper($firstBit{0}).substr($firstBit, 1).'_';
+ foreach ($nameBits as $bit) {
+ if ($bit !== '') {
+ $newName .= strtoupper($bit{0}).substr($bit, 1).'_';
+ }
+ }
+
+ $error = 'Subpackage name "%s" is not valid; consider "%s" instead';
+ $validName = trim($newName, '_');
+ $data = array(
+ $content,
+ $validName,
+ );
+ $phpcsFile->addError($error, $tag, 'InvalidSubpackage', $data);
+ }//end foreach
+
+ }//end processSubpackage()
+
+
+ /**
+ * Process the author tag(s) that this header comment has.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $tags The tokens for these tags.
+ *
+ * @return void
+ */
+ protected function processAuthor($phpcsFile, array $tags)
+ {
+ $tokens = $phpcsFile->getTokens();
+ foreach ($tags as $tag) {
+ if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
+ // No content.
+ continue;
+ }
+
+ $content = $tokens[($tag + 2)]['content'];
+ $local = '\da-zA-Z-_+';
+ // Dot character cannot be the first or last character in the local-part.
+ $localMiddle = $local.'.\w';
+ if (preg_match('/^([^<]*)\s+<(['.$local.'](['.$localMiddle.']*['.$local.'])*@[\da-zA-Z][-.\w]*[\da-zA-Z]\.[a-zA-Z]{2,7})>$/', $content) === 0) {
+ $error = 'Content of the @author tag must be in the form "Display Name <username@example.com>"';
+ $phpcsFile->addError($error, $tag, 'InvalidAuthors');
+ }
+ }
+
+ }//end processAuthor()
+
+
+ /**
+ * Process the copyright tags.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $tags The tokens for these tags.
+ *
+ * @return void
+ */
+ protected function processCopyright($phpcsFile, array $tags)
+ {
+ $tokens = $phpcsFile->getTokens();
+ foreach ($tags as $tag) {
+ if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
+ // No content.
+ continue;
+ }
+
+ $content = $tokens[($tag + 2)]['content'];
+ $matches = array();
+ if (preg_match('/^([0-9]{4})((.{1})([0-9]{4}))? (.+)$/', $content, $matches) !== 0) {
+ // Check earliest-latest year order.
+ if ($matches[3] !== '' && $matches[3] !== null) {
+ if ($matches[3] !== '-') {
+ $error = 'A hyphen must be used between the earliest and latest year';
+ $phpcsFile->addError($error, $tag, 'CopyrightHyphen');
+ }
+
+ if ($matches[4] !== '' && $matches[4] !== null && $matches[4] < $matches[1]) {
+ $error = "Invalid year span \"$matches[1]$matches[3]$matches[4]\" found; consider \"$matches[4]-$matches[1]\" instead";
+ $phpcsFile->addWarning($error, $tag, 'InvalidCopyright');
+ }
+ }
+ } else {
+ $error = '@copyright tag must contain a year and the name of the copyright holder';
+ $phpcsFile->addError($error, $tag, 'IncompleteCopyright');
+ }
+ }//end foreach
+
+ }//end processCopyright()
+
+
+ /**
+ * Process the license tag.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $tags The tokens for these tags.
+ *
+ * @return void
+ */
+ protected function processLicense($phpcsFile, array $tags)
+ {
+ $tokens = $phpcsFile->getTokens();
+ foreach ($tags as $tag) {
+ if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
+ // No content.
+ continue;
+ }
+
+ $content = $tokens[($tag + 2)]['content'];
+ $matches = array();
+ preg_match('/^([^\s]+)\s+(.*)/', $content, $matches);
+ if (count($matches) !== 3) {
+ $error = '@license tag must contain a URL and a license name';
+ $phpcsFile->addError($error, $tag, 'IncompleteLicense');
+ }
+ }
+
+ }//end processLicense()
+
+
+ /**
+ * Process the version tag.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $tags The tokens for these tags.
+ *
+ * @return void
+ */
+ protected function processVersion($phpcsFile, array $tags)
+ {
+ $tokens = $phpcsFile->getTokens();
+ foreach ($tags as $tag) {
+ if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
+ // No content.
+ continue;
+ }
+
+ $content = $tokens[($tag + 2)]['content'];
+ if (strstr($content, 'CVS:') === false
+ && strstr($content, 'SVN:') === false
+ && strstr($content, 'GIT:') === false
+ && strstr($content, 'HG:') === false
+ ) {
+ $error = 'Invalid version "%s" in file comment; consider "CVS: <cvs_id>" or "SVN: <svn_id>" or "GIT: <git_id>" or "HG: <hg_id>" instead';
+ $data = array($content);
+ $phpcsFile->addWarning($error, $tag, 'InvalidVersion', $data);
+ }
+ }
+
+ }//end processVersion()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Parses and verifies the doc comments for functions.
+ *
+ * @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\Standards\PEAR\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class FunctionCommentSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_FUNCTION);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $find = Tokens::$methodPrefixes;
+ $find[] = T_WHITESPACE;
+
+ $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
+ if ($tokens[$commentEnd]['code'] === T_COMMENT) {
+ // Inline comments might just be closing comments for
+ // control structures or functions instead of function comments
+ // using the wrong comment type. If there is other code on the line,
+ // assume they relate to that code.
+ $prev = $phpcsFile->findPrevious($find, ($commentEnd - 1), null, true);
+ if ($prev !== false && $tokens[$prev]['line'] === $tokens[$commentEnd]['line']) {
+ $commentEnd = $prev;
+ }
+ }
+
+ if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
+ && $tokens[$commentEnd]['code'] !== T_COMMENT
+ ) {
+ $phpcsFile->addError('Missing function doc comment', $stackPtr, 'Missing');
+ $phpcsFile->recordMetric($stackPtr, 'Function has doc comment', 'no');
+ return;
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Function has doc comment', 'yes');
+ }
+
+ if ($tokens[$commentEnd]['code'] === T_COMMENT) {
+ $phpcsFile->addError('You must use "/**" style comments for a function comment', $stackPtr, 'WrongStyle');
+ return;
+ }
+
+ if ($tokens[$commentEnd]['line'] !== ($tokens[$stackPtr]['line'] - 1)) {
+ $error = 'There must be no blank lines after the function comment';
+ $phpcsFile->addError($error, $commentEnd, 'SpacingAfter');
+ }
+
+ $commentStart = $tokens[$commentEnd]['comment_opener'];
+ foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
+ if ($tokens[$tag]['content'] === '@see') {
+ // Make sure the tag isn't empty.
+ $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd);
+ if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) {
+ $error = 'Content missing for @see tag in function comment';
+ $phpcsFile->addError($error, $tag, 'EmptySees');
+ }
+ }
+ }
+
+ $this->processReturn($phpcsFile, $stackPtr, $commentStart);
+ $this->processThrows($phpcsFile, $stackPtr, $commentStart);
+ $this->processParams($phpcsFile, $stackPtr, $commentStart);
+
+ }//end process()
+
+
+ /**
+ * Process the return comment of this function comment.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $commentStart The position in the stack where the comment started.
+ *
+ * @return void
+ */
+ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Skip constructor and destructor.
+ $methodName = $phpcsFile->getDeclarationName($stackPtr);
+ $isSpecialMethod = ($methodName === '__construct' || $methodName === '__destruct');
+
+ $return = null;
+ foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
+ if ($tokens[$tag]['content'] === '@return') {
+ if ($return !== null) {
+ $error = 'Only 1 @return tag is allowed in a function comment';
+ $phpcsFile->addError($error, $tag, 'DuplicateReturn');
+ return;
+ }
+
+ $return = $tag;
+ }
+ }
+
+ if ($isSpecialMethod === true) {
+ return;
+ }
+
+ if ($return !== null) {
+ $content = $tokens[($return + 2)]['content'];
+ if (empty($content) === true || $tokens[($return + 2)]['code'] !== T_DOC_COMMENT_STRING) {
+ $error = 'Return type missing for @return tag in function comment';
+ $phpcsFile->addError($error, $return, 'MissingReturnType');
+ }
+ } else {
+ $error = 'Missing @return tag in function comment';
+ $phpcsFile->addError($error, $tokens[$commentStart]['comment_closer'], 'MissingReturn');
+ }//end if
+
+ }//end processReturn()
+
+
+ /**
+ * Process any throw tags that this function comment has.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $commentStart The position in the stack where the comment started.
+ *
+ * @return void
+ */
+ protected function processThrows(File $phpcsFile, $stackPtr, $commentStart)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $throws = array();
+ foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
+ if ($tokens[$tag]['content'] !== '@throws') {
+ continue;
+ }
+
+ $exception = null;
+ $comment = null;
+ if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) {
+ $matches = array();
+ preg_match('/([^\s]+)(?:\s+(.*))?/', $tokens[($tag + 2)]['content'], $matches);
+ $exception = $matches[1];
+ if (isset($matches[2]) === true) {
+ $comment = $matches[2];
+ }
+ }
+
+ if ($exception === null) {
+ $error = 'Exception type missing for @throws tag in function comment';
+ $phpcsFile->addError($error, $tag, 'InvalidThrows');
+ }
+ }//end foreach
+
+ }//end processThrows()
+
+
+ /**
+ * Process the function parameter comments.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $commentStart The position in the stack where the comment started.
+ *
+ * @return void
+ */
+ protected function processParams(File $phpcsFile, $stackPtr, $commentStart)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $params = array();
+ $maxType = 0;
+ $maxVar = 0;
+ foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) {
+ if ($tokens[$tag]['content'] !== '@param') {
+ continue;
+ }
+
+ $type = '';
+ $typeSpace = 0;
+ $var = '';
+ $varSpace = 0;
+ $comment = '';
+ $commentEnd = 0;
+ $commentTokens = array();
+
+ if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) {
+ $matches = array();
+ preg_match('/([^$&.]+)(?:((?:\.\.\.)?(?:\$|&)[^\s]+)(?:(\s+)(.*))?)?/', $tokens[($tag + 2)]['content'], $matches);
+
+ if (empty($matches) === false) {
+ $typeLen = strlen($matches[1]);
+ $type = trim($matches[1]);
+ $typeSpace = ($typeLen - strlen($type));
+ $typeLen = strlen($type);
+ if ($typeLen > $maxType) {
+ $maxType = $typeLen;
+ }
+ }
+
+ if (isset($matches[2]) === true) {
+ $var = $matches[2];
+ $varLen = strlen($var);
+ if ($varLen > $maxVar) {
+ $maxVar = $varLen;
+ }
+
+ if (isset($matches[4]) === true) {
+ $varSpace = strlen($matches[3]);
+ $comment = $matches[4];
+
+ // Any strings until the next tag belong to this comment.
+ if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) {
+ $end = $tokens[$commentStart]['comment_tags'][($pos + 1)];
+ } else {
+ $end = $tokens[$commentStart]['comment_closer'];
+ }
+
+ for ($i = ($tag + 3); $i < $end; $i++) {
+ if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
+ $comment .= ' '.$tokens[$i]['content'];
+ $commentEnd = $i;
+ $commentTokens[] = $i;
+ }
+ }
+ } else {
+ $error = 'Missing parameter comment';
+ $phpcsFile->addError($error, $tag, 'MissingParamComment');
+ }//end if
+ } else {
+ $error = 'Missing parameter name';
+ $phpcsFile->addError($error, $tag, 'MissingParamName');
+ }//end if
+ } else {
+ $error = 'Missing parameter type';
+ $phpcsFile->addError($error, $tag, 'MissingParamType');
+ }//end if
+
+ $params[] = array(
+ 'tag' => $tag,
+ 'type' => $type,
+ 'var' => $var,
+ 'comment' => $comment,
+ 'comment_end' => $commentEnd,
+ 'comment_tokens' => $commentTokens,
+ 'type_space' => $typeSpace,
+ 'var_space' => $varSpace,
+ );
+ }//end foreach
+
+ $realParams = $phpcsFile->getMethodParameters($stackPtr);
+ $foundParams = array();
+
+ // We want to use ... for all variable length arguments, so add
+ // this prefix to the variable name so comparisons are easier.
+ foreach ($realParams as $pos => $param) {
+ if ($param['variable_length'] === true) {
+ $realParams[$pos]['name'] = '...'.$realParams[$pos]['name'];
+ }
+ }
+
+ foreach ($params as $pos => $param) {
+ if ($param['var'] === '') {
+ continue;
+ }
+
+ $foundParams[] = $param['var'];
+
+ // Check number of spaces after the type.
+ $spaces = ($maxType - strlen($param['type']) + 1);
+ if ($param['type_space'] !== $spaces) {
+ $error = 'Expected %s spaces after parameter type; %s found';
+ $data = array(
+ $spaces,
+ $param['type_space'],
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $param['tag'], 'SpacingAfterParamType', $data);
+ if ($fix === true) {
+ $commentToken = ($param['tag'] + 2);
+
+ $content = $param['type'];
+ $content .= str_repeat(' ', $spaces);
+ $content .= $param['var'];
+ $content .= str_repeat(' ', $param['var_space']);
+
+ $wrapLength = ($tokens[$commentToken]['length'] - $param['type_space'] - $param['var_space'] - strlen($param['type']) - strlen($param['var']));
+
+ $star = $phpcsFile->findPrevious(T_DOC_COMMENT_STAR, $param['tag']);
+ $spaceLength = (strlen($content) + $tokens[($commentToken - 1)]['length'] + $tokens[($commentToken - 2)]['length']);
+
+ $padding = str_repeat(' ', ($tokens[$star]['column'] - 1));
+ $padding .= '* ';
+ $padding .= str_repeat(' ', $spaceLength);
+
+ $content .= wordwrap(
+ $param['comment'],
+ $wrapLength,
+ $phpcsFile->eolChar.$padding
+ );
+
+ $phpcsFile->fixer->replaceToken($commentToken, $content);
+ for ($i = ($commentToken + 1); $i <= $param['comment_end']; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+ }//end if
+ }//end if
+
+ // Make sure the param name is correct.
+ if (isset($realParams[$pos]) === true) {
+ $realName = $realParams[$pos]['name'];
+ if ($realName !== $param['var']) {
+ $code = 'ParamNameNoMatch';
+ $data = array(
+ $param['var'],
+ $realName,
+ );
+
+ $error = 'Doc comment for parameter %s does not match ';
+ if (strtolower($param['var']) === strtolower($realName)) {
+ $error .= 'case of ';
+ $code = 'ParamNameNoCaseMatch';
+ }
+
+ $error .= 'actual variable name %s';
+
+ $phpcsFile->addError($error, $param['tag'], $code, $data);
+ }
+ } else if (substr($param['var'], -4) !== ',...') {
+ // We must have an extra parameter comment.
+ $error = 'Superfluous parameter comment';
+ $phpcsFile->addError($error, $param['tag'], 'ExtraParamComment');
+ }//end if
+
+ if ($param['comment'] === '') {
+ continue;
+ }
+
+ // Check number of spaces after the param name.
+ $spaces = ($maxVar - strlen($param['var']) + 1);
+ if ($param['var_space'] !== $spaces) {
+ $error = 'Expected %s spaces after parameter name; %s found';
+ $data = array(
+ $spaces,
+ $param['var_space'],
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $param['tag'], 'SpacingAfterParamName', $data);
+ if ($fix === true) {
+ $commentToken = ($param['tag'] + 2);
+
+ $content = $param['type'];
+ $content .= str_repeat(' ', $param['type_space']);
+ $content .= $param['var'];
+ $content .= str_repeat(' ', $spaces);
+
+ $wrapLength = ($tokens[$commentToken]['length'] - $param['type_space'] - $param['var_space'] - strlen($param['type']) - strlen($param['var']));
+
+ $star = $phpcsFile->findPrevious(T_DOC_COMMENT_STAR, $param['tag']);
+ $spaceLength = (strlen($content) + $tokens[($commentToken - 1)]['length'] + $tokens[($commentToken - 2)]['length']);
+
+ $padding = str_repeat(' ', ($tokens[$star]['column'] - 1));
+ $padding .= '* ';
+ $padding .= str_repeat(' ', $spaceLength);
+
+ $content .= wordwrap(
+ $param['comment'],
+ $wrapLength,
+ $phpcsFile->eolChar.$padding
+ );
+
+ $phpcsFile->fixer->replaceToken($commentToken, $content);
+ for ($i = ($commentToken + 1); $i <= $param['comment_end']; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+ }//end if
+ }//end if
+
+ // Check the alignment of multi-line param comments.
+ if ($param['tag'] !== $param['comment_end']) {
+ $wrapLength = ($tokens[($param['tag'] + 2)]['length'] - $param['type_space'] - $param['var_space'] - strlen($param['type']) - strlen($param['var']));
+
+ $startColumn = ($tokens[($param['tag'] + 2)]['column'] + $tokens[($param['tag'] + 2)]['length'] - $wrapLength);
+
+ $star = $phpcsFile->findPrevious(T_DOC_COMMENT_STAR, $param['tag']);
+ $expected = ($startColumn - $tokens[$star]['column'] - 1);
+
+ foreach ($param['comment_tokens'] as $commentToken) {
+ if ($tokens[$commentToken]['column'] === $startColumn) {
+ continue;
+ }
+
+ $found = 0;
+ if ($tokens[($commentToken - 1)]['code'] === T_DOC_COMMENT_WHITESPACE) {
+ $found = $tokens[($commentToken - 1)]['length'];
+ }
+
+ $error = 'Parameter comment not aligned correctly; expected %s spaces but found %s';
+ $data = array(
+ $expected,
+ $found,
+ );
+ $fix = $phpcsFile->addFixableError($error, $commentToken, 'ParamCommentAlignment', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $expected);
+ if ($tokens[($commentToken - 1)]['code'] === T_DOC_COMMENT_WHITESPACE) {
+ $phpcsFile->fixer->replaceToken(($commentToken - 1), $padding);
+ } else {
+ $phpcsFile->fixer->addContentBefore($commentToken, $padding);
+ }
+ }
+ }//end foreach
+ }//end if
+ }//end foreach
+
+ $realNames = array();
+ foreach ($realParams as $realParam) {
+ $realNames[] = $realParam['name'];
+ }
+
+ // Report missing comments.
+ $diff = array_diff($realNames, $foundParams);
+ foreach ($diff as $neededParam) {
+ $error = 'Doc comment for parameter "%s" missing';
+ $data = array($neededParam);
+ $phpcsFile->addError($error, $commentStart, 'MissingParamTag', $data);
+ }
+
+ }//end processParams()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that no Perl-style comments are used.
+ *
+ * @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\Standards\PEAR\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class InlineCommentSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_COMMENT);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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]['content']{0} === '#') {
+ $phpcsFile->recordMetric($stackPtr, 'Inline comment style', '# ...');
+
+ $error = 'Perl-style comments are not allowed. Use "// Comment."';
+ $error .= ' or "/* comment */" instead.';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'WrongStyle');
+ if ($fix === true) {
+ $newComment = ltrim($tokens[$stackPtr]['content'], '# ');
+ $newComment = '// '.$newComment;
+ $phpcsFile->fixer->replaceToken($stackPtr, $newComment);
+ }
+ } else if ($tokens[$stackPtr]['content']{0} === '/'
+ && $tokens[$stackPtr]['content']{1} === '/'
+ ) {
+ $phpcsFile->recordMetric($stackPtr, 'Inline comment style', '// ...');
+ } else if ($tokens[$stackPtr]['content']{0} === '/'
+ && $tokens[$stackPtr]['content']{1} === '*'
+ ) {
+ $phpcsFile->recordMetric($stackPtr, 'Inline comment style', '/* ... */');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that control statements conform to their coding standards.
+ *
+ * @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\Standards\PEAR\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\AbstractPatternSniff;
+
+class ControlSignatureSniff extends AbstractPatternSniff
+{
+
+ /**
+ * If true, comments will be ignored if they are found in the code.
+ *
+ * @var boolean
+ */
+ public $ignoreComments = true;
+
+
+ /**
+ * Returns the patterns that this test wishes to verify.
+ *
+ * @return string[]
+ */
+ protected function getPatterns()
+ {
+ return array(
+ 'do {EOL...} while (...);EOL',
+ 'while (...) {EOL',
+ 'for (...) {EOL',
+ 'if (...) {EOL',
+ 'foreach (...) {EOL',
+ '} else if (...) {EOL',
+ '} elseif (...) {EOL',
+ '} else {EOL',
+ 'do {EOL',
+ );
+
+ }//end getPatterns()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure multi-line IF conditions are defined correctly.
+ *
+ * @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\Standards\PEAR\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class MultiLineConditionSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+ /**
+ * The number of spaces code should be indented.
+ *
+ * @var integer
+ */
+ public $indent = 4;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_IF,
+ T_ELSEIF,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[$stackPtr]['parenthesis_opener']) === false) {
+ return;
+ }
+
+ $openBracket = $tokens[$stackPtr]['parenthesis_opener'];
+ $closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
+ $spaceAfterOpen = 0;
+ if ($tokens[($openBracket + 1)]['code'] === T_WHITESPACE) {
+ if (strpos($tokens[($openBracket + 1)]['content'], $phpcsFile->eolChar) !== false) {
+ $spaceAfterOpen = 'newline';
+ } else {
+ $spaceAfterOpen = strlen($tokens[($openBracket + 1)]['content']);
+ }
+ }
+
+ if ($spaceAfterOpen !== 0) {
+ $error = 'First condition of a multi-line IF statement must directly follow the opening parenthesis';
+ $fix = $phpcsFile->addFixableError($error, ($openBracket + 1), 'SpacingAfterOpenBrace');
+ if ($fix === true) {
+ if ($spaceAfterOpen === 'newline') {
+ $phpcsFile->fixer->replaceToken(($openBracket + 1), '');
+ } else {
+ $phpcsFile->fixer->replaceToken(($openBracket + 1), '');
+ }
+ }
+ }
+
+ // We need to work out how far indented the if statement
+ // itself is, so we can work out how far to indent conditions.
+ $statementIndent = 0;
+ for ($i = ($stackPtr - 1); $i >= 0; $i--) {
+ if ($tokens[$i]['line'] !== $tokens[$stackPtr]['line']) {
+ $i++;
+ break;
+ }
+ }
+
+ if ($i >= 0 && $tokens[$i]['code'] === T_WHITESPACE) {
+ $statementIndent = strlen($tokens[$i]['content']);
+ }
+
+ // Each line between the parenthesis should be indented 4 spaces
+ // and start with an operator, unless the line is inside a
+ // function call, in which case it is ignored.
+ $prevLine = $tokens[$openBracket]['line'];
+ for ($i = ($openBracket + 1); $i <= $closeBracket; $i++) {
+ if ($i === $closeBracket && $tokens[$openBracket]['line'] !== $tokens[$i]['line']) {
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($i - 1), null, true);
+ if ($tokens[$prev]['line'] === $tokens[$i]['line']) {
+ // Closing bracket is on the same line as a condition.
+ $error = 'Closing parenthesis of a multi-line IF statement must be on a new line';
+ $fix = $phpcsFile->addFixableError($error, $closeBracket, 'CloseBracketNewLine');
+ if ($fix === true) {
+ // Account for a comment at the end of the line.
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($closeBracket + 1), null, true);
+ if ($tokens[$next]['code'] !== T_COMMENT) {
+ $phpcsFile->fixer->addNewlineBefore($closeBracket);
+ } else {
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken($closeBracket, '');
+ $phpcsFile->fixer->addContentBefore($next, ')');
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ }
+ }//end if
+
+ if ($tokens[$i]['line'] !== $prevLine) {
+ if ($tokens[$i]['line'] === $tokens[$closeBracket]['line']) {
+ $next = $phpcsFile->findNext(T_WHITESPACE, $i, null, true);
+ if ($next !== $closeBracket) {
+ $expectedIndent = ($statementIndent + $this->indent);
+ } else {
+ // Closing brace needs to be indented to the same level
+ // as the statement.
+ $expectedIndent = $statementIndent;
+ }//end if
+ } else {
+ $expectedIndent = ($statementIndent + $this->indent);
+ }//end if
+
+ if ($tokens[$i]['code'] === T_COMMENT) {
+ $prevLine = $tokens[$i]['line'];
+ continue;
+ }
+
+ // We changed lines, so this should be a whitespace indent token.
+ if ($tokens[$i]['code'] !== T_WHITESPACE) {
+ $foundIndent = 0;
+ } else {
+ $foundIndent = strlen($tokens[$i]['content']);
+ }
+
+ if ($expectedIndent !== $foundIndent) {
+ $error = 'Multi-line IF statement not indented correctly; expected %s spaces but found %s';
+ $data = array(
+ $expectedIndent,
+ $foundIndent,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $i, 'Alignment', $data);
+ if ($fix === true) {
+ $spaces = str_repeat(' ', $expectedIndent);
+ if ($foundIndent === 0) {
+ $phpcsFile->fixer->addContentBefore($i, $spaces);
+ } else {
+ $phpcsFile->fixer->replaceToken($i, $spaces);
+ }
+ }
+ }
+
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, $i, null, true);
+ if ($next !== $closeBracket) {
+ if (isset(Tokens::$booleanOperators[$tokens[$next]['code']]) === false) {
+ $error = 'Each line in a multi-line IF statement must begin with a boolean operator';
+ $fix = $phpcsFile->addFixableError($error, $i, 'StartWithBoolean');
+ if ($fix === true) {
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($i - 1), $openBracket, true);
+ if (isset(Tokens::$booleanOperators[$tokens[$prev]['code']]) === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken($prev, '');
+ $phpcsFile->fixer->addContentBefore($next, $tokens[$prev]['content'].' ');
+ $phpcsFile->fixer->endChangeset();
+ } else {
+ for ($x = ($prev + 1); $x < $next; $x++) {
+ $phpcsFile->fixer->replaceToken($x, '');
+ }
+ }
+ }
+ }
+ }//end if
+
+ $prevLine = $tokens[$i]['line'];
+ }//end if
+
+ if ($tokens[$i]['code'] === T_STRING) {
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), null, true);
+ if ($tokens[$next]['code'] === T_OPEN_PARENTHESIS) {
+ // This is a function call, so skip to the end as they
+ // have their own indentation rules.
+ $i = $tokens[$next]['parenthesis_closer'];
+ $prevLine = $tokens[$i]['line'];
+ continue;
+ }
+ }
+ }//end for
+
+ // From here on, we are checking the spacing of the opening and closing
+ // braces. If this IF statement does not use braces, we end here.
+ if (isset($tokens[$stackPtr]['scope_opener']) === false) {
+ return;
+ }
+
+ // The opening brace needs to be one space away from the closing parenthesis.
+ $openBrace = $tokens[$stackPtr]['scope_opener'];
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($closeBracket + 1), $openBrace, true);
+ if ($next !== false) {
+ // Probably comments in between tokens, so don't check.
+ return;
+ }
+
+ if ($tokens[$openBrace]['line'] > $tokens[$closeBracket]['line']) {
+ $length = -1;
+ } else if ($openBrace === ($closeBracket + 1)) {
+ $length = 0;
+ } else if ($openBrace === ($closeBracket + 2)
+ && $tokens[($closeBracket + 1)]['code'] === T_WHITESPACE
+ ) {
+ $length = strlen($tokens[($closeBracket + 1)]['content']);
+ } else {
+ // Confused, so don't check.
+ $length = 1;
+ }
+
+ if ($length === 1) {
+ return;
+ }
+
+ $data = array($length);
+ $code = 'SpaceBeforeOpenBrace';
+
+ $error = 'There must be a single space between the closing parenthesis and the opening brace of a multi-line IF statement; found ';
+ if ($length === -1) {
+ $error .= 'newline';
+ $code = 'NewlineBeforeOpenBrace';
+ } else {
+ $error .= '%s spaces';
+ }
+
+ $fix = $phpcsFile->addFixableError($error, ($closeBracket + 1), $code, $data);
+ if ($fix === true) {
+ if ($length === 0) {
+ $phpcsFile->fixer->addContent($closeBracket, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($closeBracket + 1), ' ');
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure include_once is used in conditional situations and require_once is used elsewhere.
+ *
+ * Also checks that brackets do not surround the file being included.
+ *
+ * @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\Standards\PEAR\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class IncludingFileSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_INCLUDE_ONCE,
+ T_REQUIRE_ONCE,
+ T_REQUIRE,
+ T_INCLUDE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+ if ($tokens[$nextToken]['code'] === T_OPEN_PARENTHESIS) {
+ $error = '"%s" is a statement not a function; no parentheses are required';
+ $data = array($tokens[$stackPtr]['content']);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'BracketsNotRequired', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken($tokens[$nextToken]['parenthesis_closer'], '');
+ if ($tokens[($nextToken - 1)]['code'] !== T_WHITESPACE) {
+ $phpcsFile->fixer->replaceToken($nextToken, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken($nextToken, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ if (count($tokens[$stackPtr]['conditions']) !== 0) {
+ $inCondition = true;
+ } else {
+ $inCondition = false;
+ }
+
+ // Check to see if this including statement is within the parenthesis
+ // of a condition. If that's the case then we need to process it as being
+ // within a condition, as they are checking the return value.
+ if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
+ foreach ($tokens[$stackPtr]['nested_parenthesis'] as $left => $right) {
+ if (isset($tokens[$left]['parenthesis_owner']) === true) {
+ $inCondition = true;
+ }
+ }
+ }
+
+ // Check to see if they are assigning the return value of this
+ // including call. If they are then they are probably checking it, so
+ // it's conditional.
+ $previous = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if (isset(Tokens::$assignmentTokens[$tokens[$previous]['code']]) === true) {
+ // The have assigned the return value to it, so its conditional.
+ $inCondition = true;
+ }
+
+ $tokenCode = $tokens[$stackPtr]['code'];
+ if ($inCondition === true) {
+ // We are inside a conditional statement. We need an include_once.
+ if ($tokenCode === T_REQUIRE_ONCE) {
+ $error = 'File is being conditionally included; ';
+ $error .= 'use "include_once" instead';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'UseIncludeOnce');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, 'include_once');
+ }
+ } else if ($tokenCode === T_REQUIRE) {
+ $error = 'File is being conditionally included; ';
+ $error .= 'use "include" instead';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'UseInclude');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, 'include');
+ }
+ }
+ } else {
+ // We are unconditionally including, we need a require_once.
+ if ($tokenCode === T_INCLUDE_ONCE) {
+ $error = 'File is being unconditionally included; ';
+ $error .= 'use "require_once" instead';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'UseRequireOnce');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, 'require_once');
+ }
+ } else if ($tokenCode === T_INCLUDE) {
+ $error = 'File is being unconditionally included; ';
+ $error .= 'use "require" instead';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'UseRequire');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, 'require');
+ }
+ }
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * If an assignment goes over two lines, ensure the equal sign is indented.
+ *
+ * @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\Standards\PEAR\Sniffs\Formatting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class MultiLineAssignmentSniff implements Sniff
+{
+
+ /**
+ * The number of spaces code should be indented.
+ *
+ * @var integer
+ */
+ public $indent = 4;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_EQUAL);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // Equal sign can't be the last thing on the line.
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($next === false) {
+ // Bad assignment.
+ return;
+ }
+
+ if ($tokens[$next]['line'] !== $tokens[$stackPtr]['line']) {
+ $error = 'Multi-line assignments must have the equal sign on the second line';
+ $phpcsFile->addError($error, $stackPtr, 'EqualSignLine');
+ return;
+ }
+
+ // Make sure it is the first thing on the line, otherwise we ignore it.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), false, true);
+ if ($prev === false) {
+ // Bad assignment.
+ return;
+ }
+
+ if ($tokens[$prev]['line'] === $tokens[$stackPtr]['line']) {
+ return;
+ }
+
+ // Find the required indent based on the ident of the previous line.
+ $assignmentIndent = 0;
+ $prevLine = $tokens[$prev]['line'];
+ for ($i = ($prev - 1); $i >= 0; $i--) {
+ if ($tokens[$i]['line'] !== $prevLine) {
+ $i++;
+ break;
+ }
+ }
+
+ if ($tokens[$i]['code'] === T_WHITESPACE) {
+ $assignmentIndent = strlen($tokens[$i]['content']);
+ }
+
+ // Find the actual indent.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1));
+
+ $expectedIndent = ($assignmentIndent + $this->indent);
+ $foundIndent = strlen($tokens[$prev]['content']);
+ if ($foundIndent !== $expectedIndent) {
+ $error = 'Multi-line assignment not indented correctly; expected %s spaces but found %s';
+ $data = array(
+ $expectedIndent,
+ $foundIndent,
+ );
+ $phpcsFile->addError($error, $stackPtr, 'Indent', $data);
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures function calls are formatted correctly.
+ *
+ * @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\Standards\PEAR\Sniffs\Functions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class FunctionCallSignatureSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+ /**
+ * The number of spaces code should be indented.
+ *
+ * @var integer
+ */
+ public $indent = 4;
+
+ /**
+ * If TRUE, multiple arguments can be defined per line in a multi-line call.
+ *
+ * @var boolean
+ */
+ public $allowMultipleArguments = true;
+
+ /**
+ * How many spaces should follow the opening bracket.
+ *
+ * @var integer
+ */
+ public $requiredSpacesAfterOpen = 0;
+
+ /**
+ * How many spaces should precede the closing bracket.
+ *
+ * @var integer
+ */
+ public $requiredSpacesBeforeClose = 0;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ $tokens = Tokens::$functionNameTokens;
+
+ $tokens[] = T_VARIABLE;
+ $tokens[] = T_CLOSE_CURLY_BRACKET;
+ $tokens[] = T_CLOSE_PARENTHESIS;
+
+ return $tokens;
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $this->requiredSpacesAfterOpen = (int) $this->requiredSpacesAfterOpen;
+ $this->requiredSpacesBeforeClose = (int) $this->requiredSpacesBeforeClose;
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['code'] === T_CLOSE_CURLY_BRACKET
+ && isset($tokens[$stackPtr]['scope_condition']) === true
+ ) {
+ // Not a function call.
+ return;
+ }
+
+ // Find the next non-empty token.
+ $openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+
+ if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
+ // Not a function call.
+ return;
+ }
+
+ if (isset($tokens[$openBracket]['parenthesis_closer']) === false) {
+ // Not a function call.
+ return;
+ }
+
+ // Find the previous non-empty token.
+ $search = Tokens::$emptyTokens;
+ $search[] = T_BITWISE_AND;
+ $previous = $phpcsFile->findPrevious($search, ($stackPtr - 1), null, true);
+ if ($tokens[$previous]['code'] === T_FUNCTION) {
+ // It's a function definition, not a function call.
+ return;
+ }
+
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+
+ if (($stackPtr + 1) !== $openBracket) {
+ // Checking this: $value = my_function[*](...).
+ $error = 'Space before opening parenthesis of function call prohibited';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceBeforeOpenBracket');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($stackPtr + 1); $i < $openBracket; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ // Modify the bracket as well to ensure a conflict if the bracket
+ // has been changed in some way by another sniff.
+ $phpcsFile->fixer->replaceToken($openBracket, '(');
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($closeBracket + 1), null, true);
+ if ($tokens[$next]['code'] === T_SEMICOLON) {
+ if (isset(Tokens::$emptyTokens[$tokens[($closeBracket + 1)]['code']]) === true) {
+ $error = 'Space after closing parenthesis of function call prohibited';
+ $fix = $phpcsFile->addFixableError($error, $closeBracket, 'SpaceAfterCloseBracket');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($closeBracket + 1); $i < $next; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ // Modify the bracket as well to ensure a conflict if the bracket
+ // has been changed in some way by another sniff.
+ $phpcsFile->fixer->replaceToken($closeBracket, ')');
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ }
+
+ // Check if this is a single line or multi-line function call.
+ if ($this->isMultiLineCall($phpcsFile, $stackPtr, $openBracket, $tokens) === true) {
+ $this->processMultiLineCall($phpcsFile, $stackPtr, $openBracket, $tokens);
+ } else {
+ $this->processSingleLineCall($phpcsFile, $stackPtr, $openBracket, $tokens);
+ }
+
+ }//end process()
+
+
+ /**
+ * Determine if this is a multi-line function call.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $openBracket The position of the opening bracket
+ * in the stack passed in $tokens.
+ * @param array $tokens The stack of tokens that make up
+ * the file.
+ *
+ * @return void
+ */
+ public function isMultiLineCall(File $phpcsFile, $stackPtr, $openBracket, $tokens)
+ {
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+ if ($tokens[$openBracket]['line'] !== $tokens[$closeBracket]['line']) {
+ return true;
+ }
+
+ return false;
+
+ }//end isMultiLineCall()
+
+
+ /**
+ * Processes single-line calls.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $openBracket The position of the opening bracket
+ * in the stack passed in $tokens.
+ * @param array $tokens The stack of tokens that make up
+ * the file.
+ *
+ * @return void
+ */
+ public function processSingleLineCall(File $phpcsFile, $stackPtr, $openBracket, $tokens)
+ {
+ // If the function call has no arguments or comments, enforce 0 spaces.
+ $closer = $tokens[$openBracket]['parenthesis_closer'];
+ if ($openBracket === ($closer - 1)) {
+ return;
+ }
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($openBracket + 1), $closer, true);
+ if ($next === false) {
+ $requiredSpacesAfterOpen = 0;
+ $requiredSpacesBeforeClose = 0;
+ } else {
+ $requiredSpacesAfterOpen = $this->requiredSpacesAfterOpen;
+ $requiredSpacesBeforeClose = $this->requiredSpacesBeforeClose;
+ }
+
+ if ($requiredSpacesAfterOpen === 0 && $tokens[($openBracket + 1)]['code'] === T_WHITESPACE) {
+ // Checking this: $value = my_function([*]...).
+ $error = 'Space after opening parenthesis of function call prohibited';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterOpenBracket');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($openBracket + 1), '');
+ }
+ } else if ($requiredSpacesAfterOpen > 0) {
+ $spaceAfterOpen = 0;
+ if ($tokens[($openBracket + 1)]['code'] === T_WHITESPACE) {
+ $spaceAfterOpen = strlen($tokens[($openBracket + 1)]['content']);
+ }
+
+ if ($spaceAfterOpen !== $requiredSpacesAfterOpen) {
+ $error = 'Expected %s spaces after opening bracket; %s found';
+ $data = array(
+ $requiredSpacesAfterOpen,
+ $spaceAfterOpen,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterOpenBracket', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $requiredSpacesAfterOpen);
+ if ($spaceAfterOpen === 0) {
+ $phpcsFile->fixer->addContent($openBracket, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($openBracket + 1), $padding);
+ }
+ }
+ }
+ }//end if
+
+ // Checking this: $value = my_function(...[*]).
+ $spaceBeforeClose = 0;
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($closer - 1), $openBracket, true);
+ if ($tokens[$prev]['code'] === T_END_HEREDOC || $tokens[$prev]['code'] === T_END_NOWDOC) {
+ // Need a newline after these tokens, so ignore this rule.
+ return;
+ }
+
+ if ($tokens[$prev]['line'] !== $tokens[$closer]['line']) {
+ $spaceBeforeClose = 'newline';
+ } else if ($tokens[($closer - 1)]['code'] === T_WHITESPACE) {
+ $spaceBeforeClose = strlen($tokens[($closer - 1)]['content']);
+ }
+
+ if ($spaceBeforeClose !== $requiredSpacesBeforeClose) {
+ $error = 'Expected %s spaces before closing bracket; %s found';
+ $data = array(
+ $requiredSpacesBeforeClose,
+ $spaceBeforeClose,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceBeforeCloseBracket', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $requiredSpacesBeforeClose);
+
+ if ($spaceBeforeClose === 0) {
+ $phpcsFile->fixer->addContentBefore($closer, $padding);
+ } else if ($spaceBeforeClose === 'newline') {
+ $phpcsFile->fixer->beginChangeset();
+
+ $closingContent = ')';
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($closer + 1), null, true);
+ if ($tokens[$next]['code'] === T_SEMICOLON) {
+ $closingContent .= ';';
+ for ($i = ($closer + 1); $i <= $next; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+ }
+
+ // We want to jump over any whitespace or inline comment and
+ // move the closing parenthesis after any other token.
+ $prev = ($closer - 1);
+ while (isset(Tokens::$emptyTokens[$tokens[$prev]['code']]) === true) {
+ if (($tokens[$prev]['code'] === T_COMMENT)
+ && (strpos($tokens[$prev]['content'], '*/') !== false)
+ ) {
+ break;
+ }
+
+ $prev--;
+ }
+
+ $phpcsFile->fixer->addContent($prev, $padding.$closingContent);
+
+ $prevNonWhitespace = $phpcsFile->findPrevious(T_WHITESPACE, ($closer - 1), null, true);
+ for ($i = ($prevNonWhitespace + 1); $i <= $closer; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ } else {
+ $phpcsFile->fixer->replaceToken(($closer - 1), $padding);
+ }//end if
+ }//end if
+ }//end if
+
+ }//end processSingleLineCall()
+
+
+ /**
+ * Processes multi-line calls.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $openBracket The position of the opening bracket
+ * in the stack passed in $tokens.
+ * @param array $tokens The stack of tokens that make up
+ * the file.
+ *
+ * @return void
+ */
+ public function processMultiLineCall(File $phpcsFile, $stackPtr, $openBracket, $tokens)
+ {
+ // We need to work out how far indented the function
+ // call itself is, so we can work out how far to
+ // indent the arguments.
+ $start = $phpcsFile->findStartOfStatement($stackPtr);
+ foreach (array('stackPtr', 'start') as $checkToken) {
+ $x = $$checkToken;
+ for ($i = ($x - 1); $i >= 0; $i--) {
+ if ($tokens[$i]['line'] !== $tokens[$x]['line']) {
+ $i++;
+ break;
+ }
+ }
+
+ if ($i <= 0) {
+ $functionIndent = 0;
+ } else if ($tokens[$i]['code'] === T_WHITESPACE) {
+ $functionIndent = strlen($tokens[$i]['content']);
+ } else if ($tokens[$i]['code'] === T_CONSTANT_ENCAPSED_STRING) {
+ $functionIndent = 0;
+ } else {
+ $trimmed = ltrim($tokens[$i]['content']);
+ if ($trimmed === '') {
+ if ($tokens[$i]['code'] === T_INLINE_HTML) {
+ $functionIndent = strlen($tokens[$i]['content']);
+ } else {
+ $functionIndent = ($tokens[$i]['column'] - 1);
+ }
+ } else {
+ $functionIndent = (strlen($tokens[$i]['content']) - strlen($trimmed));
+ }
+ }
+
+ $varName = $checkToken.'Indent';
+ $$varName = $functionIndent;
+ }//end foreach
+
+ $functionIndent = max($startIndent, $stackPtrIndent);
+
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($openBracket + 1), null, true);
+ if ($tokens[$next]['line'] === $tokens[$openBracket]['line']) {
+ $error = 'Opening parenthesis of a multi-line function call must be the last content on the line';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'ContentAfterOpenBracket');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent(
+ $openBracket,
+ $phpcsFile->eolChar.str_repeat(' ', ($functionIndent + $this->indent))
+ );
+ }
+ }
+
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBracket - 1), null, true);
+ if ($tokens[$prev]['line'] === $tokens[$closeBracket]['line']) {
+ $error = 'Closing parenthesis of a multi-line function call must be on a line by itself';
+ $fix = $phpcsFile->addFixableError($error, $closeBracket, 'CloseBracketLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContentBefore(
+ $closeBracket,
+ $phpcsFile->eolChar.str_repeat(' ', ($functionIndent + $this->indent))
+ );
+ }
+ }
+
+ // Each line between the parenthesis should be indented n spaces.
+ $lastLine = ($tokens[$openBracket]['line'] - 1);
+ $argStart = null;
+ $argEnd = null;
+ $inArg = false;
+
+ // Start processing at the first argument.
+ $i = $phpcsFile->findNext(T_WHITESPACE, ($openBracket + 1), null, true);
+ if ($tokens[($i - 1)]['code'] === T_WHITESPACE
+ && $tokens[($i - 1)]['line'] === $tokens[$i]['line']
+ ) {
+ // Make sure we check the indent.
+ $i--;
+ }
+
+ for ($i; $i < $closeBracket; $i++) {
+ if ($i > $argStart && $i < $argEnd) {
+ $inArg = true;
+ } else {
+ $inArg = false;
+ }
+
+ if ($tokens[$i]['line'] !== $lastLine) {
+ $lastLine = $tokens[$i]['line'];
+
+ // Ignore heredoc indentation.
+ if (isset(Tokens::$heredocTokens[$tokens[$i]['code']]) === true) {
+ continue;
+ }
+
+ // Ignore multi-line string indentation.
+ if (isset(Tokens::$stringTokens[$tokens[$i]['code']]) === true
+ && $tokens[$i]['code'] === $tokens[($i - 1)]['code']
+ ) {
+ continue;
+ }
+
+ // Ignore inline HTML.
+ if ($tokens[$i]['code'] === T_INLINE_HTML) {
+ continue;
+ }
+
+ if ($tokens[$i]['line'] !== $tokens[$openBracket]['line']) {
+ // We changed lines, so this should be a whitespace indent token, but first make
+ // sure it isn't a blank line because we don't need to check indent unless there
+ // is actually some code to indent.
+ if ($tokens[$i]['code'] === T_WHITESPACE) {
+ $nextCode = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), ($closeBracket + 1), true);
+ if ($tokens[$nextCode]['line'] !== $lastLine) {
+ if ($inArg === false) {
+ $error = 'Empty lines are not allowed in multi-line function calls';
+ $fix = $phpcsFile->addFixableError($error, $i, 'EmptyLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+ }
+
+ continue;
+ }
+ } else {
+ $nextCode = $i;
+ }
+
+ if ($tokens[$nextCode]['line'] === $tokens[$closeBracket]['line']) {
+ // Closing brace needs to be indented to the same level
+ // as the function call.
+ $inArg = false;
+ $expectedIndent = $functionIndent;
+ } else {
+ $expectedIndent = ($functionIndent + $this->indent);
+ }
+
+ if ($tokens[$i]['code'] !== T_WHITESPACE
+ && $tokens[$i]['code'] !== T_DOC_COMMENT_WHITESPACE
+ ) {
+ // Just check if it is a multi-line block comment. If so, we can
+ // calculate the indent from the whitespace before the content.
+ if ($tokens[$i]['code'] === T_COMMENT
+ && $tokens[($i - 1)]['code'] === T_COMMENT
+ ) {
+ $trimmedLength = strlen(ltrim($tokens[$i]['content']));
+ if ($trimmedLength === 0) {
+ // This is a blank comment line, so indenting it is
+ // pointless.
+ continue;
+ }
+
+ $foundIndent = (strlen($tokens[$i]['content']) - $trimmedLength);
+ } else {
+ $foundIndent = 0;
+ }
+ } else {
+ $foundIndent = strlen($tokens[$i]['content']);
+ }
+
+ if ($foundIndent < $expectedIndent
+ || ($inArg === false
+ && $expectedIndent !== $foundIndent)
+ ) {
+ $error = 'Multi-line function call not indented correctly; expected %s spaces but found %s';
+ $data = array(
+ $expectedIndent,
+ $foundIndent,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $i, 'Indent', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $expectedIndent);
+ if ($foundIndent === 0) {
+ $phpcsFile->fixer->addContentBefore($i, $padding);
+ } else {
+ if ($tokens[$i]['code'] === T_COMMENT) {
+ $comment = $padding.ltrim($tokens[$i]['content']);
+ $phpcsFile->fixer->replaceToken($i, $comment);
+ } else {
+ $phpcsFile->fixer->replaceToken($i, $padding);
+ }
+ }
+ }
+ }//end if
+ } else {
+ $nextCode = $i;
+ }//end if
+
+ if ($inArg === false) {
+ $argStart = $nextCode;
+ $argEnd = $phpcsFile->findEndOfStatement($nextCode);
+ }
+ }//end if
+
+ // If we are within an argument we should be ignoring commas
+ // as these are not signaling the end of an argument.
+ if ($inArg === false && $tokens[$i]['code'] === T_COMMA) {
+ $next = $phpcsFile->findNext(array(T_WHITESPACE, T_COMMENT), ($i + 1), $closeBracket, true);
+ if ($next === false) {
+ continue;
+ }
+
+ if ($this->allowMultipleArguments === false) {
+ // Comma has to be the last token on the line.
+ if ($tokens[$i]['line'] === $tokens[$next]['line']) {
+ $error = 'Only one argument is allowed per line in a multi-line function call';
+ $fix = $phpcsFile->addFixableError($error, $next, 'MultipleArguments');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($x = ($next - 1); $x > $i; $x--) {
+ if ($tokens[$x]['code'] !== T_WHITESPACE) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($x, '');
+ }
+
+ $phpcsFile->fixer->addContentBefore(
+ $next,
+ $phpcsFile->eolChar.str_repeat(' ', ($functionIndent + $this->indent))
+ );
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ }//end if
+
+ $argStart = $next;
+ $argEnd = $phpcsFile->findEndOfStatement($next);
+ }//end if
+ }//end for
+
+ }//end processMultiLineCall()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure single and multi-line function declarations are defined correctly.
+ *
+ * @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\Standards\PEAR\Sniffs\Functions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Standards\Generic\Sniffs\Functions\OpeningFunctionBraceKernighanRitchieSniff;
+use PHP_CodeSniffer\Standards\Generic\Sniffs\Functions\OpeningFunctionBraceBsdAllmanSniff;
+
+class FunctionDeclarationSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+ /**
+ * The number of spaces code should be indented.
+ *
+ * @var integer
+ */
+ public $indent = 4;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_FUNCTION,
+ T_CLOSURE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[$stackPtr]['parenthesis_opener']) === false
+ || isset($tokens[$stackPtr]['parenthesis_closer']) === false
+ || $tokens[$stackPtr]['parenthesis_opener'] === null
+ || $tokens[$stackPtr]['parenthesis_closer'] === null
+ ) {
+ return;
+ }
+
+ $openBracket = $tokens[$stackPtr]['parenthesis_opener'];
+ $closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
+
+ if (strtolower($tokens[$stackPtr]['content']) === 'function') {
+ // Must be one space after the FUNCTION keyword.
+ if ($tokens[($stackPtr + 1)]['content'] === $phpcsFile->eolChar) {
+ $spaces = 'newline';
+ } else if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
+ $spaces = strlen($tokens[($stackPtr + 1)]['content']);
+ } else {
+ $spaces = 0;
+ }
+
+ if ($spaces !== 1) {
+ $error = 'Expected 1 space after FUNCTION keyword; %s found';
+ $data = array($spaces);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterFunction', $data);
+ if ($fix === true) {
+ if ($spaces === 0) {
+ $phpcsFile->fixer->addContent($stackPtr, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+ }
+ }//end if
+
+ // Must be one space before the opening parenthesis. For closures, this is
+ // enforced by the first check because there is no content between the keywords
+ // and the opening parenthesis.
+ if ($tokens[$stackPtr]['code'] === T_FUNCTION) {
+ if ($tokens[($openBracket - 1)]['content'] === $phpcsFile->eolChar) {
+ $spaces = 'newline';
+ } else if ($tokens[($openBracket - 1)]['code'] === T_WHITESPACE) {
+ $spaces = strlen($tokens[($openBracket - 1)]['content']);
+ } else {
+ $spaces = 0;
+ }
+
+ if ($spaces !== 0) {
+ $error = 'Expected 0 spaces before opening parenthesis; %s found';
+ $data = array($spaces);
+ $fix = $phpcsFile->addFixableError($error, $openBracket, 'SpaceBeforeOpenParen', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($openBracket - 1), '');
+ }
+ }
+ }//end if
+
+ // Must be one space before and after USE keyword for closures.
+ if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
+ $use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']);
+ if ($use !== false) {
+ if ($tokens[($use + 1)]['code'] !== T_WHITESPACE) {
+ $length = 0;
+ } else if ($tokens[($use + 1)]['content'] === "\t") {
+ $length = '\t';
+ } else {
+ $length = strlen($tokens[($use + 1)]['content']);
+ }
+
+ if ($length !== 1) {
+ $error = 'Expected 1 space after USE keyword; found %s';
+ $data = array($length);
+ $fix = $phpcsFile->addFixableError($error, $use, 'SpaceAfterUse', $data);
+ if ($fix === true) {
+ if ($length === 0) {
+ $phpcsFile->fixer->addContent($use, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($use + 1), ' ');
+ }
+ }
+ }
+
+ if ($tokens[($use - 1)]['code'] !== T_WHITESPACE) {
+ $length = 0;
+ } else if ($tokens[($use - 1)]['content'] === "\t") {
+ $length = '\t';
+ } else {
+ $length = strlen($tokens[($use - 1)]['content']);
+ }
+
+ if ($length !== 1) {
+ $error = 'Expected 1 space before USE keyword; found %s';
+ $data = array($length);
+ $fix = $phpcsFile->addFixableError($error, $use, 'SpaceBeforeUse', $data);
+ if ($fix === true) {
+ if ($length === 0) {
+ $phpcsFile->fixer->addContentBefore($use, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($use - 1), ' ');
+ }
+ }
+ }
+ }//end if
+ }//end if
+
+ if ($this->isMultiLineDeclaration($phpcsFile, $stackPtr, $openBracket, $tokens) === true) {
+ $this->processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens);
+ } else {
+ $this->processSingleLineDeclaration($phpcsFile, $stackPtr, $tokens);
+ }
+
+ }//end process()
+
+
+ /**
+ * Determine if this is a multi-line function declaration.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $openBracket The position of the opening bracket
+ * in the stack passed in $tokens.
+ * @param array $tokens The stack of tokens that make up
+ * the file.
+ *
+ * @return void
+ */
+ public function isMultiLineDeclaration($phpcsFile, $stackPtr, $openBracket, $tokens)
+ {
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+ if ($tokens[$openBracket]['line'] !== $tokens[$closeBracket]['line']) {
+ return true;
+ }
+
+ // Closures may use the USE keyword and so be multi-line in this way.
+ if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
+ $use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']);
+ if ($use !== false) {
+ // If the opening and closing parenthesis of the use statement
+ // are also on the same line, this is a single line declaration.
+ $open = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1));
+ $close = $tokens[$open]['parenthesis_closer'];
+ if ($tokens[$open]['line'] !== $tokens[$close]['line']) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+
+ }//end isMultiLineDeclaration()
+
+
+ /**
+ * Processes single-line declarations.
+ *
+ * Just uses the Generic BSD-Allman brace sniff.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param array $tokens The stack of tokens that make up
+ * the file.
+ *
+ * @return void
+ */
+ public function processSingleLineDeclaration($phpcsFile, $stackPtr, $tokens)
+ {
+ if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
+ $sniff = new OpeningFunctionBraceKernighanRitchieSniff();
+ } else {
+ $sniff = new OpeningFunctionBraceBsdAllmanSniff();
+ }
+
+ $sniff->checkClosures = true;
+ $sniff->process($phpcsFile, $stackPtr);
+
+ }//end processSingleLineDeclaration()
+
+
+ /**
+ * Processes multi-line declarations.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param array $tokens The stack of tokens that make up
+ * the file.
+ *
+ * @return void
+ */
+ public function processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens)
+ {
+ // We need to work out how far indented the function
+ // declaration itself is, so we can work out how far to
+ // indent parameters.
+ $functionIndent = 0;
+ for ($i = ($stackPtr - 1); $i >= 0; $i--) {
+ if ($tokens[$i]['line'] !== $tokens[$stackPtr]['line']) {
+ $i++;
+ break;
+ }
+ }
+
+ if ($tokens[$i]['code'] === T_WHITESPACE) {
+ $functionIndent = strlen($tokens[$i]['content']);
+ }
+
+ // The closing parenthesis must be on a new line, even
+ // when checking abstract function definitions.
+ $closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
+ $prev = $phpcsFile->findPrevious(
+ T_WHITESPACE,
+ ($closeBracket - 1),
+ null,
+ true
+ );
+
+ if ($tokens[$closeBracket]['line'] !== $tokens[$tokens[$closeBracket]['parenthesis_opener']]['line']) {
+ if ($tokens[$prev]['line'] === $tokens[$closeBracket]['line']) {
+ $error = 'The closing parenthesis of a multi-line function declaration must be on a new line';
+ $fix = $phpcsFile->addFixableError($error, $closeBracket, 'CloseBracketLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewlineBefore($closeBracket);
+ }
+ }
+ }
+
+ // If this is a closure and is using a USE statement, the closing
+ // parenthesis we need to look at from now on is the closing parenthesis
+ // of the USE statement.
+ if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
+ $use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']);
+ if ($use !== false) {
+ $open = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1));
+ $closeBracket = $tokens[$open]['parenthesis_closer'];
+
+ $prev = $phpcsFile->findPrevious(
+ T_WHITESPACE,
+ ($closeBracket - 1),
+ null,
+ true
+ );
+
+ if ($tokens[$closeBracket]['line'] !== $tokens[$tokens[$closeBracket]['parenthesis_opener']]['line']) {
+ if ($tokens[$prev]['line'] === $tokens[$closeBracket]['line']) {
+ $error = 'The closing parenthesis of a multi-line use declaration must be on a new line';
+ $fix = $phpcsFile->addFixableError($error, $closeBracket, 'UseCloseBracketLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewlineBefore($closeBracket);
+ }
+ }
+ }
+ }//end if
+ }//end if
+
+ // Each line between the parenthesis should be indented 4 spaces.
+ $openBracket = $tokens[$stackPtr]['parenthesis_opener'];
+ $lastLine = $tokens[$openBracket]['line'];
+ for ($i = ($openBracket + 1); $i < $closeBracket; $i++) {
+ if ($tokens[$i]['line'] !== $lastLine) {
+ if ($i === $tokens[$stackPtr]['parenthesis_closer']
+ || ($tokens[$i]['code'] === T_WHITESPACE
+ && (($i + 1) === $closeBracket
+ || ($i + 1) === $tokens[$stackPtr]['parenthesis_closer']))
+ ) {
+ // Closing braces need to be indented to the same level
+ // as the function.
+ $expectedIndent = $functionIndent;
+ } else {
+ $expectedIndent = ($functionIndent + $this->indent);
+ }
+
+ // We changed lines, so this should be a whitespace indent token.
+ if ($tokens[$i]['code'] !== T_WHITESPACE) {
+ $foundIndent = 0;
+ } else if ($tokens[$i]['line'] !== $tokens[($i + 1)]['line']) {
+ // This is an empty line, so don't check the indent.
+ $foundIndent = $expectedIndent;
+
+ $error = 'Blank lines are not allowed in a multi-line function declaration';
+ $fix = $phpcsFile->addFixableError($error, $i, 'EmptyLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+ } else {
+ $foundIndent = strlen($tokens[$i]['content']);
+ }
+
+ if ($expectedIndent !== $foundIndent) {
+ $error = 'Multi-line function declaration not indented correctly; expected %s spaces but found %s';
+ $data = array(
+ $expectedIndent,
+ $foundIndent,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $i, 'Indent', $data);
+ if ($fix === true) {
+ $spaces = str_repeat(' ', $expectedIndent);
+ if ($foundIndent === 0) {
+ $phpcsFile->fixer->addContentBefore($i, $spaces);
+ } else {
+ $phpcsFile->fixer->replaceToken($i, $spaces);
+ }
+ }
+ }
+
+ $lastLine = $tokens[$i]['line'];
+ }//end if
+
+ if ($tokens[$i]['code'] === T_ARRAY || $tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) {
+ // Skip arrays as they have their own indentation rules.
+ if ($tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) {
+ $i = $tokens[$i]['bracket_closer'];
+ } else {
+ $i = $tokens[$i]['parenthesis_closer'];
+ }
+
+ $lastLine = $tokens[$i]['line'];
+ continue;
+ }
+ }//end for
+
+ if (isset($tokens[$stackPtr]['scope_opener']) === false) {
+ return;
+ }
+
+ // The opening brace needs to be one space away from the closing parenthesis.
+ $opener = $tokens[$stackPtr]['scope_opener'];
+ if ($tokens[$opener]['line'] !== $tokens[$closeBracket]['line']) {
+ $error = 'The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line';
+ $fix = $phpcsFile->addFixableError($error, $opener, 'NewlineBeforeOpenBrace');
+ if ($fix === true) {
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), $closeBracket, true);
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->addContent($prev, ' {');
+
+ // If the opener is on a line by itself, removing it will create
+ // an empty line, so just remove the entire line instead.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($opener - 1), $closeBracket, true);
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($opener + 1), null, true);
+
+ if ($tokens[$prev]['line'] < $tokens[$opener]['line']
+ && $tokens[$next]['line'] > $tokens[$opener]['line']
+ ) {
+ // Clear the whole line.
+ for ($i = ($prev + 1); $i < $next; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$opener]['line']) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+ }
+ } else {
+ // Just remove the opener.
+ $phpcsFile->fixer->replaceToken($opener, '');
+ if ($tokens[$next]['line'] === $tokens[$opener]['line']) {
+ $phpcsFile->fixer->replaceToken(($opener + 1), '');
+ }
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+ } else {
+ $prev = $tokens[($opener - 1)];
+ if ($prev['code'] !== T_WHITESPACE) {
+ $length = 0;
+ } else {
+ $length = strlen($prev['content']);
+ }
+
+ if ($length !== 1) {
+ $error = 'There must be a single space between the closing parenthesis and the opening brace of a multi-line function declaration; found %s spaces';
+ $fix = $phpcsFile->addFixableError($error, ($opener - 1), 'SpaceBeforeOpenBrace', array($length));
+ if ($fix === true) {
+ if ($length === 0) {
+ $phpcsFile->fixer->addContentBefore($opener, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($opener - 1), ' ');
+ }
+ }
+
+ return;
+ }//end if
+ }//end if
+
+ }//end processMultiLineDeclaration()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures function params with default values are at the end of the declaration.
+ *
+ * @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\Standards\PEAR\Sniffs\Functions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ValidDefaultValueSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(
+ T_FUNCTION,
+ T_CLOSURE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $argStart = $tokens[$stackPtr]['parenthesis_opener'];
+ $argEnd = $tokens[$stackPtr]['parenthesis_closer'];
+
+ // Flag for when we have found a default in our arg list.
+ // If there is a value without a default after this, it is an error.
+ $defaultFound = false;
+
+ $params = $phpcsFile->getMethodParameters($stackPtr);
+ foreach ($params as $param) {
+ if ($param['variable_length'] === true) {
+ continue;
+ }
+
+ if (array_key_exists('default', $param) === true) {
+ $defaultFound = true;
+ // Check if the arg is type hinted and using NULL for the default.
+ // This does not make the argument optional - it just allows NULL
+ // to be passed in.
+ if ($param['type_hint'] !== '' && strtolower($param['default']) === 'null') {
+ $defaultFound = false;
+ }
+
+ continue;
+ }
+
+ if ($defaultFound === true) {
+ $error = 'Arguments with default values must be at the end of the argument list';
+ $phpcsFile->addError($error, $param['token'], 'NotAtEnd');
+ return;
+ }
+ }//end foreach
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures class and interface names start with a capital letter and use _ separators.
+ *
+ * @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\Standards\PEAR\Sniffs\NamingConventions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ValidClassNameSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_CLASS,
+ T_INTERFACE,
+ T_TRAIT,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being processed.
+ * @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();
+
+ $className = $phpcsFile->findNext(T_STRING, $stackPtr);
+ $name = trim($tokens[$className]['content']);
+ $errorData = array(ucfirst($tokens[$stackPtr]['content']));
+
+ // Make sure the first letter is a capital.
+ if (preg_match('|^[A-Z]|', $name) === 0) {
+ $error = '%s name must begin with a capital letter';
+ $phpcsFile->addError($error, $stackPtr, 'StartWithCapital', $errorData);
+ }
+
+ // Check that each new word starts with a capital as well, but don't
+ // check the first word, as it is checked above.
+ $validName = true;
+ $nameBits = explode('_', $name);
+ $firstBit = array_shift($nameBits);
+ foreach ($nameBits as $bit) {
+ if ($bit === '' || $bit{0} !== strtoupper($bit{0})) {
+ $validName = false;
+ break;
+ }
+ }
+
+ if ($validName === false) {
+ // Strip underscores because they cause the suggested name
+ // to be incorrect.
+ $nameBits = explode('_', trim($name, '_'));
+ $firstBit = array_shift($nameBits);
+ if ($firstBit === '') {
+ $error = '%s name is not valid';
+ $phpcsFile->addError($error, $stackPtr, 'Invalid', $errorData);
+ } else {
+ $newName = strtoupper($firstBit{0}).substr($firstBit, 1).'_';
+ foreach ($nameBits as $bit) {
+ if ($bit !== '') {
+ $newName .= strtoupper($bit{0}).substr($bit, 1).'_';
+ }
+ }
+
+ $newName = rtrim($newName, '_');
+ $error = '%s name is not valid; consider %s instead';
+ $data = $errorData;
+ $data[] = $newName;
+ $phpcsFile->addError($error, $stackPtr, 'Invalid', $data);
+ }
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures method and function names are correct.
+ *
+ * @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\Standards\PEAR\Sniffs\NamingConventions;
+
+use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
+use PHP_CodeSniffer\Util\Common;
+use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Files\File;
+
+class ValidFunctionNameSniff extends AbstractScopeSniff
+{
+
+ /**
+ * A list of all PHP magic methods.
+ *
+ * @var array
+ */
+ protected $magicMethods = array(
+ 'construct' => true,
+ 'destruct' => true,
+ 'call' => true,
+ 'callstatic' => true,
+ 'get' => true,
+ 'set' => true,
+ 'isset' => true,
+ 'unset' => true,
+ 'sleep' => true,
+ 'wakeup' => true,
+ 'tostring' => true,
+ 'set_state' => true,
+ 'clone' => true,
+ 'invoke' => true,
+ 'debuginfo' => true,
+ );
+
+ /**
+ * A list of all PHP magic functions.
+ *
+ * @var array
+ */
+ protected $magicFunctions = array('autoload' => true);
+
+
+ /**
+ * Constructs a PEAR_Sniffs_NamingConventions_ValidFunctionNameSniff.
+ */
+ public function __construct()
+ {
+ parent::__construct(Tokens::$ooScopeTokens, array(T_FUNCTION), true);
+
+ }//end __construct()
+
+
+ /**
+ * Processes the tokens within the scope.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed.
+ * @param int $stackPtr The position where this token was
+ * found.
+ * @param int $currScope The position of the current scope.
+ *
+ * @return void
+ */
+ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
+ {
+ $methodName = $phpcsFile->getDeclarationName($stackPtr);
+ if ($methodName === null) {
+ // Ignore closures.
+ return;
+ }
+
+ $className = $phpcsFile->getDeclarationName($currScope);
+ $errorData = array($className.'::'.$methodName);
+
+ // Is this a magic method. i.e., is prefixed with "__" ?
+ if (preg_match('|^__[^_]|', $methodName) !== 0) {
+ $magicPart = strtolower(substr($methodName, 2));
+ if (isset($this->magicMethods[$magicPart]) === false) {
+ $error = 'Method name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore';
+ $phpcsFile->addError($error, $stackPtr, 'MethodDoubleUnderscore', $errorData);
+ }
+
+ return;
+ }
+
+ // PHP4 constructors are allowed to break our rules.
+ if ($methodName === $className) {
+ return;
+ }
+
+ // PHP4 destructors are allowed to break our rules.
+ if ($methodName === '_'.$className) {
+ return;
+ }
+
+ $methodProps = $phpcsFile->getMethodProperties($stackPtr);
+ $scope = $methodProps['scope'];
+ $scopeSpecified = $methodProps['scope_specified'];
+
+ if ($methodProps['scope'] === 'private') {
+ $isPublic = false;
+ } else {
+ $isPublic = true;
+ }
+
+ // If it's a private method, it must have an underscore on the front.
+ if ($isPublic === false) {
+ if ($methodName{0} !== '_') {
+ $error = 'Private method name "%s" must be prefixed with an underscore';
+ $phpcsFile->addError($error, $stackPtr, 'PrivateNoUnderscore', $errorData);
+ $phpcsFile->recordMetric($stackPtr, 'Private method prefixed with underscore', 'no');
+ return;
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Private method prefixed with underscore', 'yes');
+ }
+ }
+
+ // If it's not a private method, it must not have an underscore on the front.
+ if ($isPublic === true && $scopeSpecified === true && $methodName{0} === '_') {
+ $error = '%s method name "%s" must not be prefixed with an underscore';
+ $data = array(
+ ucfirst($scope),
+ $errorData[0],
+ );
+ $phpcsFile->addError($error, $stackPtr, 'PublicUnderscore', $data);
+ return;
+ }
+
+ // If the scope was specified on the method, then the method must be
+ // camel caps and an underscore should be checked for. If it wasn't
+ // specified, treat it like a public method and remove the underscore
+ // prefix if there is one because we cant determine if it is private or
+ // public.
+ $testMethodName = $methodName;
+ if ($scopeSpecified === false && $methodName{0} === '_') {
+ $testMethodName = substr($methodName, 1);
+ }
+
+ if (Common::isCamelCaps($testMethodName, false, $isPublic, false) === false) {
+ if ($scopeSpecified === true) {
+ $error = '%s method name "%s" is not in camel caps format';
+ $data = array(
+ ucfirst($scope),
+ $errorData[0],
+ );
+ $phpcsFile->addError($error, $stackPtr, 'ScopeNotCamelCaps', $data);
+ } else {
+ $error = 'Method name "%s" is not in camel caps format';
+ $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData);
+ }
+
+ return;
+ }
+
+ }//end processTokenWithinScope()
+
+
+ /**
+ * Processes the tokens outside the scope.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed.
+ * @param int $stackPtr The position where this token was
+ * found.
+ *
+ * @return void
+ */
+ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
+ {
+ $functionName = $phpcsFile->getDeclarationName($stackPtr);
+ if ($functionName === null) {
+ // Ignore closures.
+ return;
+ }
+
+ if (ltrim($functionName, '_') === '') {
+ // Ignore special functions.
+ return;
+ }
+
+ $errorData = array($functionName);
+
+ // Is this a magic function. i.e., it is prefixed with "__".
+ if (preg_match('|^__[^_]|', $functionName) !== 0) {
+ $magicPart = strtolower(substr($functionName, 2));
+ if (isset($this->magicFunctions[$magicPart]) === false) {
+ $error = 'Function name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore';
+ $phpcsFile->addError($error, $stackPtr, 'FunctionDoubleUnderscore', $errorData);
+ }
+
+ return;
+ }
+
+ // Function names can be in two parts; the package name and
+ // the function name.
+ $packagePart = '';
+ $camelCapsPart = '';
+ $underscorePos = strrpos($functionName, '_');
+ if ($underscorePos === false) {
+ $camelCapsPart = $functionName;
+ } else {
+ $packagePart = substr($functionName, 0, $underscorePos);
+ $camelCapsPart = substr($functionName, ($underscorePos + 1));
+
+ // We don't care about _'s on the front.
+ $packagePart = ltrim($packagePart, '_');
+ }
+
+ // If it has a package part, make sure the first letter is a capital.
+ if ($packagePart !== '') {
+ if ($functionName{0} === '_') {
+ $error = 'Function name "%s" is invalid; only private methods should be prefixed with an underscore';
+ $phpcsFile->addError($error, $stackPtr, 'FunctionUnderscore', $errorData);
+ return;
+ }
+
+ if ($functionName{0} !== strtoupper($functionName{0})) {
+ $error = 'Function name "%s" is prefixed with a package name but does not begin with a capital letter';
+ $phpcsFile->addError($error, $stackPtr, 'FunctionNoCapital', $errorData);
+ return;
+ }
+ }
+
+ // If it doesn't have a camel caps part, it's not valid.
+ if (trim($camelCapsPart) === '') {
+ $error = 'Function name "%s" is not valid; name appears incomplete';
+ $phpcsFile->addError($error, $stackPtr, 'FunctionInvalid', $errorData);
+ return;
+ }
+
+ $validName = true;
+ $newPackagePart = $packagePart;
+ $newCamelCapsPart = $camelCapsPart;
+
+ // Every function must have a camel caps part, so check that first.
+ if (Common::isCamelCaps($camelCapsPart, false, true, false) === false) {
+ $validName = false;
+ $newCamelCapsPart = strtolower($camelCapsPart{0}).substr($camelCapsPart, 1);
+ }
+
+ if ($packagePart !== '') {
+ // Check that each new word starts with a capital.
+ $nameBits = explode('_', $packagePart);
+ foreach ($nameBits as $bit) {
+ if ($bit{0} !== strtoupper($bit{0})) {
+ $newPackagePart = '';
+ foreach ($nameBits as $bit) {
+ $newPackagePart .= strtoupper($bit{0}).substr($bit, 1).'_';
+ }
+
+ $validName = false;
+ break;
+ }
+ }
+ }
+
+ if ($validName === false) {
+ $newName = rtrim($newPackagePart, '_').'_'.$newCamelCapsPart;
+ if ($newPackagePart === '') {
+ $newName = $newCamelCapsPart;
+ } else {
+ $newName = rtrim($newPackagePart, '_').'_'.$newCamelCapsPart;
+ }
+
+ $error = 'Function name "%s" is invalid; consider "%s" instead';
+ $data = $errorData;
+ $data[] = $newName;
+ $phpcsFile->addError($error, $stackPtr, 'FunctionNameInvalid', $data);
+ }
+
+ }//end processTokenOutsideScope()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the naming of member variables.
+ *
+ * @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\Standards\PEAR\Sniffs\NamingConventions;
+
+use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
+use PHP_CodeSniffer\Files\File;
+
+class ValidVariableNameSniff extends AbstractVariableSniff
+{
+
+
+ /**
+ * Processes class member variables.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ protected function processMemberVar(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $memberProps = $phpcsFile->getMemberProperties($stackPtr);
+ if (empty($memberProps) === true) {
+ return;
+ }
+
+ $memberName = ltrim($tokens[$stackPtr]['content'], '$');
+ $scope = $memberProps['scope'];
+ $scopeSpecified = $memberProps['scope_specified'];
+
+ if ($memberProps['scope'] === 'private') {
+ $isPublic = false;
+ } else {
+ $isPublic = true;
+ }
+
+ // If it's a private member, it must have an underscore on the front.
+ if ($isPublic === false && $memberName{0} !== '_') {
+ $error = 'Private member variable "%s" must be prefixed with an underscore';
+ $data = array($memberName);
+ $phpcsFile->addError($error, $stackPtr, 'PrivateNoUnderscore', $data);
+ return;
+ }
+
+ // If it's not a private member, it must not have an underscore on the front.
+ if ($isPublic === true && $scopeSpecified === true && $memberName{0} === '_') {
+ $error = '%s member variable "%s" must not be prefixed with an underscore';
+ $data = array(
+ ucfirst($scope),
+ $memberName,
+ );
+ $phpcsFile->addError($error, $stackPtr, 'PublicUnderscore', $data);
+ return;
+ }
+
+ }//end processMemberVar()
+
+
+ /**
+ * Processes normal variables.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position where the token was found.
+ *
+ * @return void
+ */
+ protected function processVariable(File $phpcsFile, $stackPtr)
+ {
+ /*
+ We don't care about normal variables.
+ */
+
+ }//end processVariable()
+
+
+ /**
+ * Processes variables in double quoted strings.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position where the token was found.
+ *
+ * @return void
+ */
+ protected function processVariableInString(File $phpcsFile, $stackPtr)
+ {
+ /*
+ We don't care about normal variables.
+ */
+
+ }//end processVariableInString()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that object operators are indented correctly.
+ *
+ * @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\Standards\PEAR\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ObjectOperatorIndentSniff implements Sniff
+{
+
+ /**
+ * The number of spaces code should be indented.
+ *
+ * @var integer
+ */
+ public $indent = 4;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OBJECT_OPERATOR);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document.
+ * @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();
+
+ // Make sure this is the first object operator in a chain of them.
+ $varToken = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($varToken === false || $tokens[$varToken]['code'] !== T_VARIABLE) {
+ return;
+ }
+
+ // Make sure this is a chained call.
+ $next = $phpcsFile->findNext(
+ T_OBJECT_OPERATOR,
+ ($stackPtr + 1),
+ null,
+ false,
+ null,
+ true
+ );
+
+ if ($next === false) {
+ // Not a chained call.
+ return;
+ }
+
+ // Determine correct indent.
+ for ($i = ($varToken - 1); $i >= 0; $i--) {
+ if ($tokens[$i]['line'] !== $tokens[$varToken]['line']) {
+ $i++;
+ break;
+ }
+ }
+
+ $requiredIndent = 0;
+ if ($i >= 0 && $tokens[$i]['code'] === T_WHITESPACE) {
+ $requiredIndent = strlen($tokens[$i]['content']);
+ }
+
+ $requiredIndent += $this->indent;
+
+ // Determine the scope of the original object operator.
+ $origBrackets = null;
+ if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
+ $origBrackets = $tokens[$stackPtr]['nested_parenthesis'];
+ }
+
+ $origConditions = null;
+ if (isset($tokens[$stackPtr]['conditions']) === true) {
+ $origConditions = $tokens[$stackPtr]['conditions'];
+ }
+
+ // Check indentation of each object operator in the chain.
+ // If the first object operator is on a different line than
+ // the variable, make sure we check its indentation too.
+ if ($tokens[$stackPtr]['line'] > $tokens[$varToken]['line']) {
+ $next = $stackPtr;
+ }
+
+ while ($next !== false) {
+ // Make sure it is in the same scope, otherwise don't check indent.
+ $brackets = null;
+ if (isset($tokens[$next]['nested_parenthesis']) === true) {
+ $brackets = $tokens[$next]['nested_parenthesis'];
+ }
+
+ $conditions = null;
+ if (isset($tokens[$next]['conditions']) === true) {
+ $conditions = $tokens[$next]['conditions'];
+ }
+
+ if ($origBrackets === $brackets && $origConditions === $conditions) {
+ // Make sure it starts a line, otherwise dont check indent.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($next - 1), $stackPtr, true);
+ $indent = $tokens[($next - 1)];
+ if ($tokens[$prev]['line'] !== $tokens[$next]['line']
+ && $indent['code'] === T_WHITESPACE
+ ) {
+ if ($indent['line'] === $tokens[$next]['line']) {
+ $foundIndent = strlen($indent['content']);
+ } else {
+ $foundIndent = 0;
+ }
+
+ if ($foundIndent !== $requiredIndent) {
+ $error = 'Object operator not indented correctly; expected %s spaces but found %s';
+ $data = array(
+ $requiredIndent,
+ $foundIndent,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $next, 'Incorrect', $data);
+ if ($fix === true) {
+ $spaces = str_repeat(' ', $requiredIndent);
+ if ($foundIndent === 0) {
+ $phpcsFile->fixer->addContentBefore($next, $spaces);
+ } else {
+ $phpcsFile->fixer->replaceToken(($next - 1), $spaces);
+ }
+ }
+ }
+ }//end if
+
+ // It cant be the last thing on the line either.
+ $content = $phpcsFile->findNext(T_WHITESPACE, ($next + 1), null, true);
+ if ($tokens[$content]['line'] !== $tokens[$next]['line']) {
+ $error = 'Object operator must be at the start of the line, not the end';
+ $fix = $phpcsFile->addFixableError($error, $next, 'StartOfLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($x = ($next + 1); $x < $content; $x++) {
+ $phpcsFile->fixer->replaceToken($x, '');
+ }
+
+ $phpcsFile->fixer->addNewlineBefore($next);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ }//end if
+
+ $next = $phpcsFile->findNext(
+ T_OBJECT_OPERATOR,
+ ($next + 1),
+ null,
+ false,
+ null,
+ true
+ );
+ }//end while
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that the closing braces of scopes are aligned correctly.
+ *
+ * @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\Standards\PEAR\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ScopeClosingBraceSniff implements Sniff
+{
+
+ /**
+ * The number of spaces code should be indented.
+ *
+ * @var integer
+ */
+ public $indent = 4;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return Tokens::$scopeOpeners;
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document.
+ * @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 this is an inline condition (ie. there is no scope opener), then
+ // return, as this is not a new scope.
+ if (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ return;
+ }
+
+ $scopeStart = $tokens[$stackPtr]['scope_opener'];
+ $scopeEnd = $tokens[$stackPtr]['scope_closer'];
+
+ // If the scope closer doesn't think it belongs to this scope opener
+ // then the opener is sharing its closer with other tokens. We only
+ // want to process the closer once, so skip this one.
+ if (isset($tokens[$scopeEnd]['scope_condition']) === false
+ || $tokens[$scopeEnd]['scope_condition'] !== $stackPtr
+ ) {
+ return;
+ }
+
+ // We need to actually find the first piece of content on this line,
+ // because if this is a method with tokens before it (public, static etc)
+ // or an if with an else before it, then we need to start the scope
+ // checking from there, rather than the current token.
+ $lineStart = ($stackPtr - 1);
+ for ($lineStart; $lineStart > 0; $lineStart--) {
+ if (strpos($tokens[$lineStart]['content'], $phpcsFile->eolChar) !== false) {
+ break;
+ }
+ }
+
+ $lineStart++;
+
+ $startColumn = 1;
+ if ($tokens[$lineStart]['code'] === T_WHITESPACE) {
+ $startColumn = $tokens[($lineStart + 1)]['column'];
+ } else if ($tokens[$lineStart]['code'] === T_INLINE_HTML) {
+ $trimmed = ltrim($tokens[$lineStart]['content']);
+ if ($trimmed === '') {
+ $startColumn = $tokens[($lineStart + 1)]['column'];
+ } else {
+ $startColumn = (strlen($tokens[$lineStart]['content']) - strlen($trimmed));
+ }
+ }
+
+ // Check that the closing brace is on it's own line.
+ $lastContent = $phpcsFile->findPrevious(
+ array(
+ T_WHITESPACE,
+ T_INLINE_HTML,
+ T_OPEN_TAG,
+ ),
+ ($scopeEnd - 1),
+ $scopeStart,
+ true
+ );
+
+ if ($tokens[$lastContent]['line'] === $tokens[$scopeEnd]['line']) {
+ $error = 'Closing brace must be on a line by itself';
+ $fix = $phpcsFile->addFixableError($error, $scopeEnd, 'Line');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewlineBefore($scopeEnd);
+ }
+
+ return;
+ }
+
+ // Check now that the closing brace is lined up correctly.
+ $lineStart = ($scopeEnd - 1);
+ for ($lineStart; $lineStart > 0; $lineStart--) {
+ if (strpos($tokens[$lineStart]['content'], $phpcsFile->eolChar) !== false) {
+ break;
+ }
+ }
+
+ $lineStart++;
+
+ $braceIndent = 0;
+ if ($tokens[$lineStart]['code'] === T_WHITESPACE) {
+ $braceIndent = ($tokens[($lineStart + 1)]['column'] - 1);
+ } else if ($tokens[$lineStart]['code'] === T_INLINE_HTML) {
+ $trimmed = ltrim($tokens[$lineStart]['content']);
+ if ($trimmed === '') {
+ $braceIndent = ($tokens[($lineStart + 1)]['column'] - 1);
+ } else {
+ $braceIndent = (strlen($tokens[$lineStart]['content']) - strlen($trimmed) - 1);
+ }
+ }
+
+ $fix = false;
+ if ($tokens[$stackPtr]['code'] === T_CASE
+ || $tokens[$stackPtr]['code'] === T_DEFAULT
+ ) {
+ // BREAK statements should be indented n spaces from the
+ // CASE or DEFAULT statement.
+ $expectedIndent = ($startColumn + $this->indent - 1);
+ if ($braceIndent !== $expectedIndent) {
+ $error = 'Case breaking statement indented incorrectly; expected %s spaces, found %s';
+ $data = array(
+ $expectedIndent,
+ $braceIndent,
+ );
+ $fix = $phpcsFile->addFixableError($error, $scopeEnd, 'BreakIndent', $data);
+ }
+ } else {
+ $expectedIndent = max(0, ($startColumn - 1));
+ if ($braceIndent !== $expectedIndent) {
+ $error = 'Closing brace indented incorrectly; expected %s spaces, found %s';
+ $data = array(
+ $expectedIndent,
+ $braceIndent,
+ );
+ $fix = $phpcsFile->addFixableError($error, $scopeEnd, 'Indent', $data);
+ }
+ }//end if
+
+ if ($fix === true) {
+ $spaces = str_repeat(' ', $expectedIndent);
+ if ($braceIndent === 0) {
+ $phpcsFile->fixer->addContentBefore($lineStart, $spaces);
+ } else {
+ $phpcsFile->fixer->replaceToken($lineStart, ltrim($tokens[$lineStart]['content']));
+ $phpcsFile->fixer->addContentBefore($lineStart, $spaces);
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that control structures are structured and indented correctly.
+ *
+ * @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\Standards\PEAR\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Standards\Generic\Sniffs\WhiteSpace\ScopeIndentSniff as GenericScopeIndentSniff;
+
+class ScopeIndentSniff extends GenericScopeIndentSniff
+{
+
+ /**
+ * Any scope openers that should not cause an indent.
+ *
+ * @var int[]
+ */
+ protected $nonIndentingScopes = array(T_SWITCH);
+
+}//end class
--- /dev/null
+<?php
+
+// Correct declarations.
+class CorrectClassDeclaration
+{
+
+}
+
+abstract class CorrectClassDeclarationWithExtends extends correctClassDeclaration
+{
+
+}
+
+final class CorrectClassDeclarationWithImplements implements correctClassDeclaration
+{
+
+}
+
+
+// Incorrect placement of opening braces
+class IncorrectBracePlacement {}
+class IncorrectBracePlacementWithExtends extends correctClassDeclaration {}
+class IncorrectBracePlacementWithImplements implements correctClassDeclaration {}
+
+// Incorrect code placement for opening brace.
+class IncorrectCodeAfterOpeningBrace
+{ echo phpinfo();
+
+}//end class
+
+class IncorrectClassDeclarationWithExtends extends correctClassDeclaration
+
+{
+
+}
+
+class IncorrectBracePlacement
+ {
+ }
+
+abstract class CodeSnifferFail
+ extends
+ ArrayObject
+ implements
+ Serializable,
+ Iterator,
+ Countable,
+ OuterIterator,
+ RecursiveIterator {
+}
+
+abstract class CodeSnifferFail
+ extends
+ ArrayObject
+ implements
+ Serializable,
+ Iterator,
+ Countable,
+ OuterIterator,
+ RecursiveIterator
+{
+}
+
+namespace A
+{
+ class B
+ {
+ }
+}
+
+$util->setLogger(new class {});
+
+var_dump(new class(10) extends SomeClass implements SomeInterface {
+ private $num;
+
+ public function __construct($num)
+ {
+ $this->num = $num;
+ }
+
+ use SomeTrait;
+});
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClassDeclaration 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\Standards\PEAR\Tests\Classes;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClassDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 21 => 1,
+ 22 => 1,
+ 23 => 1,
+ 27 => 1,
+ 33 => 1,
+ 38 => 1,
+ 49 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+define('OK', 1);
+
+class No_Comment
+{
+
+}//end class
+
+
+//
+// Sample class comment
+//
+//
+//
+class Invalid_Comment_Style1
+{
+
+}//end class
+
+
+/**
+ *
+ *
+ * Sample class comment
+ *
+ *
+ * Long description with extra blank line before and after
+ *
+ *
+ * @category PHP
+ * @package PHP_CodeSniffer
+ * @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
+ * @version Release: 1.0
+ * @link http://pear.php.net/package/PHP_CodeSniffer
+ */
+class Extra_Description_Newlines
+{
+
+}//end class
+
+
+/**
+ * Sample class comment
+ * @category PHP
+ * @package PHP_CodeSniffer
+ * @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
+ * @version
+ * @link http://pear.php.net/package/PHP_CodeSniffer
+ */
+class Missing_Newlines_Before_Tags
+{
+
+}//end class
+
+
+/**
+ * Simple class comment
+ *
+ * @category _wrong_category
+ * @package PHP_CodeSniffer
+ * @package ADDITIONAL PACKAGE TAG
+ * @subpackage SUBPACKAGE TAG
+ * @author Original Author <author@example.com>
+ * @author Greg Sherwood gsherwood@squiz.net
+ * @author Mr T <t@squiz.net>
+ * @author
+ * @copyright 1997~1994 The PHP Group
+ * @license http://www.php.net/license/3_0.txt
+ * @version INVALID VERSION CONTENT
+ * @see
+ * @see
+ * @link sdfsdf
+ * @see Net_Sample::Net_Sample()
+ * @see Net_Other
+ * @deprecated asd
+ * @unknown Unknown tag
+ * @since Class available since Release 1.2.0
+ */
+class Checking_Tags
+{
+ class Sub_Class {
+
+ }//end class
+
+
+}//end class
+
+
+/**
+ *
+ *
+ */
+class Empty_Class_Doc
+{
+
+}//end class
+
+
+/**
+ *
+ *
+ */
+interface Empty_Interface_Doc
+{
+
+}//end interface
+
+
+/**
+ *
+ *
+ */
+trait Empty_Trait_Doc
+{
+
+}//end trait
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClassComment 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\Standards\PEAR\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClassCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 4 => 1,
+ 15 => 1,
+ 51 => 1,
+ 63 => 1,
+ 65 => 2,
+ 66 => 1,
+ 68 => 1,
+ 70 => 1,
+ 71 => 1,
+ 72 => 1,
+ 74 => 2,
+ 75 => 1,
+ 76 => 1,
+ 77 => 1,
+ 85 => 1,
+ 96 => 5,
+ 106 => 5,
+ 116 => 5,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 71 => 1,
+ 73 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+declare(encoding='utf-8');
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+*
+* Short description for fileasdasd.
+*
+*
+* asdasd
+* long description for file (if any)
+* asdasdadada
+*
+* PHP versio
+*
+* LICENSE: This source file is subject to version 3.0 of the PHP license
+* that is available through the world-wide-web at the following URI:
+* http://www.php.net/license/3_0.txt. If you did not receive a copy of
+* the PHP License and are unable to obtain it through the web, please
+* send a note to license@php.net so we can mail you a copy immediately.
+* @category _wrong_category
+* @package PHP_CodeSniffer
+* @package ADDITIONAL PACKAGE TAG
+* @subpackage SUBPACKAGE TAG
+* @author Antônio Carlos Venâncio Júnior <foreign@character.net>
+* @author Greg Sherwood gsherwood@squiz.net
+* @author Mr T <t@squiz.net>
+* @author
+* @copyright 1997~1994 The PHP Group
+* @copyright 1997~1994 The PHP Group
+* @license http://www.php.net/license/3_0.txt
+* @see
+* @see
+* @version INVALID VERSION CONTENT
+* @see Net_Sample::Net_Sample()
+* @see Net_Other
+* @deprecated asd
+* @since Class available since Release 1.2.0
+* @summary An unknown summary tag
+* @package ''
+* @subpackage !!
+*/
+require_once '/some/path.php';
+?>
+<?php
+/**
+* This bit here is not qualified as file comment
+*
+* as it is not after the first open tag
+*
+*/
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the FileComment 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\Standards\PEAR\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FileCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 21 => 1,
+ 23 => 2,
+ 24 => 1,
+ 26 => 1,
+ 28 => 1,
+ 29 => 1,
+ 30 => 1,
+ 31 => 1,
+ 32 => 2,
+ 33 => 1,
+ 34 => 1,
+ 35 => 1,
+ 40 => 2,
+ 41 => 2,
+ 42 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 29 => 1,
+ 30 => 1,
+ 34 => 1,
+ 42 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class PHP_CodeSniffer_File
+{
+
+ /**
+ * A simple function comment.
+ *
+ * long desc here
+ *
+ * @param int $stackPtr The position in @ @unknown the stack of the token
+ * that opened the scope
+ * @param int $detph How many scope levels down we are.
+ * @param int $index The index
+ * @return
+ * @throws
+ */
+ private function _functionCall($stackPtr, $depth=1, $index)
+ {
+ return $stackPtr;
+
+ }//end _functionCall()
+
+ //
+ // Sample function comment
+ //
+ //
+ //
+ public function invalidCommentStyle()
+ {
+
+ }//end invalidCommentStyle()
+
+
+ /**
+ *
+ *
+ * A simple function comment
+ *
+ *
+ * Long description with extra blank line before and after
+ *
+ *
+ * @return void
+ */
+ public function extraDescriptionNewlines()
+ {
+
+ }//end extraDescriptionNewlines()
+
+
+ /**
+ * A simple function comment
+ * @return void
+ */
+ public function missingNewlinesBeforeTags()
+ {
+
+ }//end missingNewlinesBeforeTags()
+
+
+ /**
+ * Access tag should not be treated as a long description
+ *
+ * @access public
+ * @return void
+ */
+ public function accessTag()
+ {
+
+ }//end accessTag()
+
+ /**
+ * Constructor
+ *
+ * No return tag
+ */
+ function PHP_CodeSniffer_File()
+ {
+ return;
+ }
+
+
+ /**
+ * Destructor
+ *
+ * No return tag too
+ */
+ function _PHP_CodeSniffer_File()
+ {
+ return;
+ }
+
+
+ /**
+ * Destructor PHP5
+ */
+ function __destruct()
+ {
+ return;
+ }
+
+
+ function missingComment()
+ {
+ return;
+ }
+
+
+ /**
+ * no return tag
+ *
+ */
+ public function noReturn($one)
+ {
+
+ }//end noReturn()
+
+
+ /**
+ * Param not immediate
+ *
+ * @return
+ * @param int $threeSpaces
+ * @param int $superfluous
+ * @param missing
+ * @param
+ */
+ public function missingDescription($threeSpaces)
+ {
+
+ }//end missingDescription()
+
+
+ /**
+ * Param not immediate
+ *
+ * @param int $one comment
+ * @param int $two comment
+ * @param string $three comment
+ *
+ * @return void
+ */
+ public function oneSpaceAfterLongestVar($one, $two, $three)
+ {
+
+ }//end oneSpaceAfterLongestVar()
+
+
+}//end class
+
+
+/**
+ * A simple function comment
+ *
+ * @param string &$str The string passed in by reference
+ * @param string $foo The string passed in by reference
+ *
+ * @return void
+ */
+public function functionOutsideClass(&$str, &$foo)
+{
+ return;
+}//end functionOutsideClass()
+
+function missingCommentOutsideClass()
+{
+ return;
+}//end missingCommentOutsideClass()
+
+
+?><?php
+function tagBeforeComment()
+{
+ return;
+}//end tagBeforeComment()
+
+
+/**
+ * no return tag
+ *
+ *
+ *
+ */
+public function noReturnOutsideClass()
+{
+
+}//end noReturnOutsideClass()
+
+
+/**
+ * Missing param comment
+ *
+ * @param int $one comment
+ *
+ * @return void
+ * @fine Unknown tag
+ */
+public function missingTwoParamComment($one, $two, $three)
+{
+
+}//end missingTwoParamComment()
+
+
+/**
+ *
+ */
+function emptyFunctionDocComment()
+{
+}//end emptyFunctionDocComment()
+
+
+/**
+ * Test function.
+ *
+ * @param string $arg1 An argument
+ *
+ * @access public
+ * @return bool
+ */
+function myFunction($arg1) {}
+
+
+/**
+ * Test function.
+ *
+ * @param string $arg1 An argument
+ *
+ * @access public
+ * @return bool
+ */
+
+echo $blah;
+
+function myFunction($arg1) {}
+
+/**
+ * Test function.
+ *
+ * @access public
+ * @return bool
+ * @throws MyException
+ * @throws MyException When I feel like it
+ */
+function myFunction() {}
+
+class MyClass() {
+ /**
+ * An abstract function.
+ *
+ * @return string[]
+ */
+ abstract final protected function myFunction();
+}
+
+/**
+ * Creates a map of tokens => line numbers for each token.
+ *
+ * @param array $tokens The array of tokens to process.
+ * @param object $tokenizer The tokenizer being used to process this file.
+ * @param string $eolChar The EOL character to use for splitting strings.
+ *
+ * @return void
+ */
+public function foo(&$tokens, $tokenizer, $eolChar)
+{
+
+}//end foo()
+
+/**
+ * Gettext.
+ *
+ */
+public function _() {
+ return $foo;
+}
+
+class Baz {
+ /**
+ * The PHP5 constructor
+ *
+ * No return tag
+ */
+ public function __construct() {
+
+ }
+}
+
+/**
+ * Complete a step.
+ *
+ * @param string $status Status of step to complete.
+ * @param array $array Array.
+ * @param string $note Optional note.
+ *
+ * @return void
+ */
+public function completeStep($status, array $array = [Class1::class, 'test'], $note = '') {
+ echo 'foo';
+}
+
+/**
+ * Variadic function.
+ *
+ * @param string $name1 Comment.
+ * @param string ...$name2 Comment.
+ *
+ * @return void
+ */
+public function myFunction(string $name1, string ...$name2) {
+}
+
+
+/**
+ * Variadic function.
+ *
+ * @param string $name1 Comment.
+ * @param string $name2 Comment.
+ *
+ * @return void
+ */
+public function myFunction(string $name1, string ...$name2) {
+}
+
+/**
+ * Completely invalid format, but should not cause PHP notices.
+ *
+ * @param $bar
+ * Comment here.
+ * @param ...
+ * Additional arguments here.
+ *
+ * @return
+ * Return value
+ *
+ */
+function foo($bar) {
+}
+
+/**
+ * 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()
+ */
+function process(File $phpcsFile, $stackPtr)
+{
+
+}//end process()
+
+/**
+ * Processes the test.
+ *
+ * @param int $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()
+ */
+function process(File $phpcsFile, $stackPtr)
+{
+
+}//end process()
--- /dev/null
+<?php
+class PHP_CodeSniffer_File
+{
+
+ /**
+ * A simple function comment.
+ *
+ * long desc here
+ *
+ * @param int $stackPtr The position in @ @unknown the stack of the token
+ * that opened the scope
+ * @param int $detph How many scope levels down we are.
+ * @param int $index The index
+ * @return
+ * @throws
+ */
+ private function _functionCall($stackPtr, $depth=1, $index)
+ {
+ return $stackPtr;
+
+ }//end _functionCall()
+
+ //
+ // Sample function comment
+ //
+ //
+ //
+ public function invalidCommentStyle()
+ {
+
+ }//end invalidCommentStyle()
+
+
+ /**
+ *
+ *
+ * A simple function comment
+ *
+ *
+ * Long description with extra blank line before and after
+ *
+ *
+ * @return void
+ */
+ public function extraDescriptionNewlines()
+ {
+
+ }//end extraDescriptionNewlines()
+
+
+ /**
+ * A simple function comment
+ * @return void
+ */
+ public function missingNewlinesBeforeTags()
+ {
+
+ }//end missingNewlinesBeforeTags()
+
+
+ /**
+ * Access tag should not be treated as a long description
+ *
+ * @access public
+ * @return void
+ */
+ public function accessTag()
+ {
+
+ }//end accessTag()
+
+ /**
+ * Constructor
+ *
+ * No return tag
+ */
+ function PHP_CodeSniffer_File()
+ {
+ return;
+ }
+
+
+ /**
+ * Destructor
+ *
+ * No return tag too
+ */
+ function _PHP_CodeSniffer_File()
+ {
+ return;
+ }
+
+
+ /**
+ * Destructor PHP5
+ */
+ function __destruct()
+ {
+ return;
+ }
+
+
+ function missingComment()
+ {
+ return;
+ }
+
+
+ /**
+ * no return tag
+ *
+ */
+ public function noReturn($one)
+ {
+
+ }//end noReturn()
+
+
+ /**
+ * Param not immediate
+ *
+ * @return
+ * @param int $threeSpaces
+ * @param int $superfluous
+ * @param missing
+ * @param
+ */
+ public function missingDescription($threeSpaces)
+ {
+
+ }//end missingDescription()
+
+
+ /**
+ * Param not immediate
+ *
+ * @param int $one comment
+ * @param int $two comment
+ * @param string $three comment
+ *
+ * @return void
+ */
+ public function oneSpaceAfterLongestVar($one, $two, $three)
+ {
+
+ }//end oneSpaceAfterLongestVar()
+
+
+}//end class
+
+
+/**
+ * A simple function comment
+ *
+ * @param string &$str The string passed in by reference
+ * @param string $foo The string passed in by reference
+ *
+ * @return void
+ */
+public function functionOutsideClass(&$str, &$foo)
+{
+ return;
+}//end functionOutsideClass()
+
+function missingCommentOutsideClass()
+{
+ return;
+}//end missingCommentOutsideClass()
+
+
+?><?php
+function tagBeforeComment()
+{
+ return;
+}//end tagBeforeComment()
+
+
+/**
+ * no return tag
+ *
+ *
+ *
+ */
+public function noReturnOutsideClass()
+{
+
+}//end noReturnOutsideClass()
+
+
+/**
+ * Missing param comment
+ *
+ * @param int $one comment
+ *
+ * @return void
+ * @fine Unknown tag
+ */
+public function missingTwoParamComment($one, $two, $three)
+{
+
+}//end missingTwoParamComment()
+
+
+/**
+ *
+ */
+function emptyFunctionDocComment()
+{
+}//end emptyFunctionDocComment()
+
+
+/**
+ * Test function.
+ *
+ * @param string $arg1 An argument
+ *
+ * @access public
+ * @return bool
+ */
+function myFunction($arg1) {}
+
+
+/**
+ * Test function.
+ *
+ * @param string $arg1 An argument
+ *
+ * @access public
+ * @return bool
+ */
+
+echo $blah;
+
+function myFunction($arg1) {}
+
+/**
+ * Test function.
+ *
+ * @access public
+ * @return bool
+ * @throws MyException
+ * @throws MyException When I feel like it
+ */
+function myFunction() {}
+
+class MyClass() {
+ /**
+ * An abstract function.
+ *
+ * @return string[]
+ */
+ abstract final protected function myFunction();
+}
+
+/**
+ * Creates a map of tokens => line numbers for each token.
+ *
+ * @param array $tokens The array of tokens to process.
+ * @param object $tokenizer The tokenizer being used to process this file.
+ * @param string $eolChar The EOL character to use for splitting strings.
+ *
+ * @return void
+ */
+public function foo(&$tokens, $tokenizer, $eolChar)
+{
+
+}//end foo()
+
+/**
+ * Gettext.
+ *
+ */
+public function _() {
+ return $foo;
+}
+
+class Baz {
+ /**
+ * The PHP5 constructor
+ *
+ * No return tag
+ */
+ public function __construct() {
+
+ }
+}
+
+/**
+ * Complete a step.
+ *
+ * @param string $status Status of step to complete.
+ * @param array $array Array.
+ * @param string $note Optional note.
+ *
+ * @return void
+ */
+public function completeStep($status, array $array = [Class1::class, 'test'], $note = '') {
+ echo 'foo';
+}
+
+/**
+ * Variadic function.
+ *
+ * @param string $name1 Comment.
+ * @param string ...$name2 Comment.
+ *
+ * @return void
+ */
+public function myFunction(string $name1, string ...$name2) {
+}
+
+
+/**
+ * Variadic function.
+ *
+ * @param string $name1 Comment.
+ * @param string $name2 Comment.
+ *
+ * @return void
+ */
+public function myFunction(string $name1, string ...$name2) {
+}
+
+/**
+ * Completely invalid format, but should not cause PHP notices.
+ *
+ * @param $bar
+ * Comment here.
+ * @param ...
+ * Additional arguments here.
+ *
+ * @return
+ * Return value
+ *
+ */
+function foo($bar) {
+}
+
+/**
+ * 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()
+ */
+function process(File $phpcsFile, $stackPtr)
+{
+
+}//end process()
+
+/**
+ * Processes the test.
+ *
+ * @param int $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()
+ */
+function process(File $phpcsFile, $stackPtr)
+{
+
+}//end process()
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionComment 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\Standards\PEAR\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 5 => 1,
+ 10 => 1,
+ 12 => 1,
+ 13 => 1,
+ 14 => 1,
+ 15 => 1,
+ 28 => 1,
+ 76 => 1,
+ 87 => 1,
+ 103 => 1,
+ 109 => 1,
+ 112 => 1,
+ 122 => 1,
+ 123 => 2,
+ 124 => 2,
+ 125 => 1,
+ 126 => 1,
+ 137 => 1,
+ 138 => 1,
+ 139 => 1,
+ 152 => 1,
+ 155 => 1,
+ 165 => 1,
+ 172 => 1,
+ 183 => 1,
+ 190 => 2,
+ 206 => 1,
+ 234 => 1,
+ 272 => 1,
+ 313 => 1,
+ 317 => 1,
+ 324 => 1,
+ 327 => 1,
+ 329 => 1,
+ 332 => 1,
+ 344 => 1,
+ 343 => 1,
+ 345 => 1,
+ 346 => 1,
+ 360 => 1,
+ 361 => 1,
+ 363 => 1,
+ 364 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// Some code goes here.
+
+// This comment contains # multiple
+// hash signs (#).
+
+/*
+ * Here is a small function comment.
+ */
+function test()
+{
+ // Some code goes here.
+
+ # This comment is banned.
+
+}//end test()
+
+/*
+ A longer comment goes here.
+ It spans multiple lines.
+*/
+
+# This is a long comment
+# that is banned.
+
+### use the code from the regex
+### over hre
+### ok?
--- /dev/null
+<?php
+
+// Some code goes here.
+
+// This comment contains # multiple
+// hash signs (#).
+
+/*
+ * Here is a small function comment.
+ */
+function test()
+{
+ // Some code goes here.
+
+ // This comment is banned.
+
+}//end test()
+
+/*
+ A longer comment goes here.
+ It spans multiple lines.
+*/
+
+// This is a long comment
+// that is banned.
+
+// use the code from the regex
+// over hre
+// ok?
--- /dev/null
+<?php
+/**
+ * Unit test class for the InlineComment 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\Standards\PEAR\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class InlineCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 15 => 1,
+ 24 => 1,
+ 25 => 1,
+ 27 => 1,
+ 28 => 1,
+ 29 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// do ... while
+$i = 0;
+do {
+ echo $i;
+} while ($i > 0);
+
+do
+{
+ echo $i;
+} while ($i > 0);
+
+do
+{
+ echo $i;
+}
+while ($i > 0);
+
+do { echo $i; } while ($i > 0);
+
+do{
+ echo $i;
+}while($i > 0);
+
+
+// while
+while ($i < 1) {
+ echo $i;
+}
+
+while($i < 1){
+ echo $i;
+}
+
+while ($i < 1) { echo $i; }
+
+
+// for
+for ($i = 1; $i < 1; $i++) {
+ echo $i;
+}
+
+for($i = 1; $i < 1; $i++){
+ echo $i;
+}
+
+for ($i = 1; $i < 1; $i++) { echo $i; }
+
+
+// foreach
+foreach ($items as $item) {
+ echo $item;
+}
+
+foreach($items as $item){
+ echo $item;
+}
+
+for ($items as $item) { echo $item; }
+
+
+// if
+if ($i == 0) {
+ $i = 1;
+}
+
+if($i == 0){
+ $i = 1;
+}
+
+if ($i == 0) { $i = 1; }
+
+
+// else
+if ($i == 0) {
+ $i = 1;
+} else {
+ $i = 0;
+}
+
+if ($i == 0) {
+ $i = 1;
+}else{
+ $i = 0;
+}
+
+if ($i == 0) { $i = 1; } else { $i = 0; }
+
+
+// else
+if ($i == 0) {
+ $i = 1;
+} else {
+ $i = 0;
+}
+
+if ($i == 0) {
+ $i = 1;
+}else{
+ $i = 0;
+}
+
+if ($i == 0) { $i = 1; } else { $i = 0; }
+
+
+// else if
+if ($i == 0) {
+ $i = 1;
+} else if ($i == 2) {
+ $i = 0;
+}
+
+if ($i == 0) {
+ $i = 1;
+} elseif ($i == 2) {
+ $i = 0;
+}
+
+if ($i == 0) {
+ $i = 1;
+}else if($i == 2){
+ $i = 0;
+}
+
+if ($i == 0) {
+ $i = 1;
+}elseif($i == 2){
+ $i = 0;
+}
+
+if ($i == 0) { $i = 1; } else if ($i == 2) { $i = 0; }
+if ($i == 0) { $i = 1; } elseif ($1 == 2) { $i = 0; }
+
+if ($i == 0) { // this is ok because comments are allowed
+ $i = 1;
+}
+
+if ($i == 0) {// this is ok because comments are allowed
+ $i = 1;
+}
+
+if ($i == 0) { /* this is ok because comments are allowed*/
+ $i = 1;
+}
+
+if ($i == 0)
+{ // this is not ok
+ $i = 1;
+}
+
+if ($i == 0) /* this is ok */ {
+}
+
+if ($i == 0) {
+}
+else {
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ControlSignature 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\Standards\PEAR\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ControlSignatureUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 9 => 1,
+ 14 => 1,
+ 20 => 1,
+ 22 => 1,
+ 32 => 1,
+ 36 => 1,
+ 44 => 1,
+ 48 => 1,
+ 56 => 1,
+ 60 => 1,
+ 68 => 1,
+ 72 => 1,
+ 84 => 1,
+ 88 => 2,
+ 100 => 1,
+ 104 => 2,
+ 122 => 2,
+ 128 => 1,
+ 132 => 3,
+ 133 => 2,
+ 147 => 1,
+ 157 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php if (isset($param)) { ?>
+ <h3>some text</h3>
+<?php }
+
+if (($condition1
+ || $condition2)
+ && $condition3
+ && $condition4
+ && $condition5
+) {
+}
+
+if (($condition1 || $condition2) && $condition3 && $condition4 && $condition5) {
+}
+
+if (($condition1 || $condition2)
+ && $condition3
+) {
+}
+
+if (
+ ($condition1 || $condition2)
+ && $condition3
+) {
+}
+
+if (($condition1
+ || $condition2)
+) {
+}
+
+if (($condition1
+ || $condition2)
+ && $condition3 &&
+ $condition4
+) {
+}
+
+if (($condition1
+ || $condition2)
+ && $condition3
+ && $condition4
+ && $condition5
+) {
+}
+
+if (($condition1
+ || $condition2)
+) {
+}
+
+if (($condition1
+ || $condition2)
+ ) {
+}
+
+if (
+ (
+ $condition1
+ || $condition2
+ )
+ && $condition3
+) {
+}
+
+
+if ( $condition1
+ || $condition2
+ || $condition3
+) {
+}
+
+if ($condition1
+ || $condition2
+ || $condition3
+) {
+} else if ($condition1
+ || $condition2
+ || $condition3
+) {
+}
+
+if ($condition1
+ || $condition2
+ || $condition3
+) {
+} elseif (
+ $condition1
+ || $condition2 &&
+ $condition3
+) {
+}
+
+if ($condition1
+ || $condition2
+|| $condition3) {
+}
+
+if ($condition1
+ || $condition2 || $condition3
+){
+}
+
+if ($condition1)
+ echo 'bar';
+
+if ($condition1
+ || $condition2
+|| $condition3)
+ echo 'bar';
+
+
+if ($condition1
+ || $condition2 || $condition3
+)
+ echo 'bar';
+
+if (!empty($post)
+ && (!empty($context['header'])
+ xor stripos($context['header'], 'Content-Type'))
+) {
+// ...
+}
+
+if ($foo)
+{
+ echo 'bar';
+}
+
+// Should be no errors even though lines are
+// not exactly aligned together. Multi-line function
+// call takes precedence.
+if (array_key_exists($key, $value)
+ && array_key_exists(
+ $key, $value2
+ )
+) {
+}
+
+if (true) :
+ $foo = true;
+endif;
+
+if ($IPP->errorCode() == 401 || // comment
+ $IPP->errorCode() == 3200) /* long comment
+ here
+ */
+{
+ return false;
+}
+
+if ($IPP->errorCode() == 401 || // comment
+ $IPP->errorCode() == 3200) // long comment here
+{
+ return false;
+}
+
+if ($IPP->errorCode() == 401
+ // Comment explaining the next condition here.
+ || $IPP->errorCode() == 3200
+) {
+ return false;
+}
+
+function bar() {
+ if ($a
+ && $b
+) {
+ return false;
+ }
+}
+
+if ($a
+ && foo(
+ 'a',
+ 'b'
+ )) {
+ return false;
+}
+
+?>
+<?php foreach ($blah as $boo) : ?>
+ <?php if ($foo): ?>
+ <?php
+ if ($bar) {
+ } else {
+ }
+ ?>
+ <?php endif; ?>
+<?php endforeach; ?>
--- /dev/null
+<?php if (isset($param)) { ?>
+ <h3>some text</h3>
+<?php }
+
+if (($condition1
+ || $condition2)
+ && $condition3
+ && $condition4
+ && $condition5
+) {
+}
+
+if (($condition1 || $condition2) && $condition3 && $condition4 && $condition5) {
+}
+
+if (($condition1 || $condition2)
+ && $condition3
+) {
+}
+
+if (($condition1 || $condition2)
+ && $condition3
+) {
+}
+
+if (($condition1
+ || $condition2)
+) {
+}
+
+if (($condition1
+ || $condition2)
+ && $condition3
+ && $condition4
+) {
+}
+
+if (($condition1
+ || $condition2)
+ && $condition3
+ && $condition4
+ && $condition5
+) {
+}
+
+if (($condition1
+ || $condition2)
+) {
+}
+
+if (($condition1
+ || $condition2)
+) {
+}
+
+if (($condition1
+ || $condition2)
+ && $condition3
+) {
+}
+
+
+if ($condition1
+ || $condition2
+ || $condition3
+) {
+}
+
+if ($condition1
+ || $condition2
+ || $condition3
+) {
+} else if ($condition1
+ || $condition2
+ || $condition3
+) {
+}
+
+if ($condition1
+ || $condition2
+ || $condition3
+) {
+} elseif ($condition1
+ || $condition2
+ && $condition3
+) {
+}
+
+if ($condition1
+ || $condition2
+ || $condition3
+) {
+}
+
+if ($condition1
+ || $condition2 || $condition3
+) {
+}
+
+if ($condition1)
+ echo 'bar';
+
+if ($condition1
+ || $condition2
+ || $condition3
+)
+ echo 'bar';
+
+
+if ($condition1
+ || $condition2 || $condition3
+)
+ echo 'bar';
+
+if (!empty($post)
+ && (!empty($context['header'])
+ xor stripos($context['header'], 'Content-Type'))
+) {
+// ...
+}
+
+if ($foo) {
+ echo 'bar';
+}
+
+// Should be no errors even though lines are
+// not exactly aligned together. Multi-line function
+// call takes precedence.
+if (array_key_exists($key, $value)
+ && array_key_exists(
+ $key, $value2
+ )
+) {
+}
+
+if (true) :
+ $foo = true;
+endif;
+
+if ($IPP->errorCode() == 401 // comment
+ || $IPP->errorCode() == 3200 /* long comment
+ here
+ */
+) {
+ return false;
+}
+
+if ($IPP->errorCode() == 401 // comment
+ || $IPP->errorCode() == 3200 // long comment here
+) {
+ return false;
+}
+
+if ($IPP->errorCode() == 401
+ // Comment explaining the next condition here.
+ || $IPP->errorCode() == 3200
+) {
+ return false;
+}
+
+function bar() {
+ if ($a
+ && $b
+ ) {
+ return false;
+ }
+}
+
+if ($a
+ && foo(
+ 'a',
+ 'b'
+ )
+) {
+ return false;
+}
+
+?>
+<?php foreach ($blah as $boo) : ?>
+ <?php if ($foo) : ?>
+ <?php
+ if ($bar) {
+ } else {
+ }
+ ?>
+ <?php endif; ?>
+<?php endforeach; ?>
--- /dev/null
+if (blah(param)) {
+
+}
+
+if ((condition1
+ || condition2)
+ && condition3
+ && condition4
+ && condition5
+) {
+}
+
+if ((condition1 || condition2) && condition3 && condition4 && condition5) {
+}
+
+if ((condition1 || condition2)
+ && condition3
+) {
+}
+
+if (
+ (condition1 || condition2)
+ && condition3
+) {
+}
+
+if ((condition1
+ || condition2)
+) {
+}
+
+if ((condition1
+ || condition2)
+ && condition3 &&
+ condition4
+) {
+}
+
+if ((condition1
+ || condition2)
+ && condition3
+ && condition4
+ && condition5
+) {
+}
+
+if (($condition1
+ || $condition2)
+) {
+}
+
+if ((condition1
+ || condition2)
+ ) {
+}
+
+if (
+ (
+ condition1
+ || condition2
+ )
+ && condition3
+) {
+}
+
+
+if ( condition1
+ || condition2
+ || condition3
+) {
+}
+
+if (condition1
+ || condition2
+ || condition3
+) {
+} else if (condition1
+ || condition2
+ || condition3
+) {
+}
+
+if (condition1
+ || condition2
+ || condition3
+) {
+} else if (
+ condition1
+ || condition2 &&
+ condition3
+) {
+}
+
+if (condition1
+ || condition2
+|| condition3) {
+}
+
+if (condition1
+ || condition2 || condition3
+){
+}
+
+if (condition1)
+ console.info('bar');
+
+if (condition1
+ || condition2
+|| condition3)
+ console.info('bar');
+
+
+if (condition1
+ || condition2 || condition3
+)
+ console.info('bar');
+
+if (!a(post)
+ && (!a(context.header)
+ ^ a(context.header, 'Content-Type'))
+) {
+// ...
+}
+
+if (foo)
+{
+ console.info('bar');
+}
+
+// Should be no errors even though lines are
+// not exactly aligned together. Multi-line function
+// call takes precedence.
+if (array_key_exists(key, value)
+ && foo.bar.baz(
+ key, value2
+ )
+) {
+}
+
+if (true) {
+ foo = true;
+};
+
+if (foo == 401 || // comment
+ bar == 3200) /* long comment
+ here
+ */
+{
+ return false;
+}
+
+if (foo == 401 || // comment
+ bar == 3200) // long comment here
+{
+ return false;
+}
+
+if (IPP.errorCode() == 401
+ // Comment explaining the next condition here.
+ || IPP.errorCode() == 3200
+) {
+ return false;
+}
+
+function bar() {
+ if (a
+ && b
+) {
+ return false;
+ }
+}
+
+if (a
+ && foo(
+ 'a',
+ 'b'
+ )) {
+ return false;
+}
--- /dev/null
+if (blah(param)) {
+
+}
+
+if ((condition1
+ || condition2)
+ && condition3
+ && condition4
+ && condition5
+) {
+}
+
+if ((condition1 || condition2) && condition3 && condition4 && condition5) {
+}
+
+if ((condition1 || condition2)
+ && condition3
+) {
+}
+
+if ((condition1 || condition2)
+ && condition3
+) {
+}
+
+if ((condition1
+ || condition2)
+) {
+}
+
+if ((condition1
+ || condition2)
+ && condition3
+ && condition4
+) {
+}
+
+if ((condition1
+ || condition2)
+ && condition3
+ && condition4
+ && condition5
+) {
+}
+
+if (($condition1
+ || $condition2)
+) {
+}
+
+if ((condition1
+ || condition2)
+) {
+}
+
+if ((condition1
+ || condition2)
+ && condition3
+) {
+}
+
+
+if (condition1
+ || condition2
+ || condition3
+) {
+}
+
+if (condition1
+ || condition2
+ || condition3
+) {
+} else if (condition1
+ || condition2
+ || condition3
+) {
+}
+
+if (condition1
+ || condition2
+ || condition3
+) {
+} else if (condition1
+ || condition2
+ && condition3
+) {
+}
+
+if (condition1
+ || condition2
+ || condition3
+) {
+}
+
+if (condition1
+ || condition2 || condition3
+) {
+}
+
+if (condition1)
+ console.info('bar');
+
+if (condition1
+ || condition2
+ || condition3
+)
+ console.info('bar');
+
+
+if (condition1
+ || condition2 || condition3
+)
+ console.info('bar');
+
+if (!a(post)
+ && (!a(context.header)
+ ^ a(context.header, 'Content-Type'))
+) {
+// ...
+}
+
+if (foo) {
+ console.info('bar');
+}
+
+// Should be no errors even though lines are
+// not exactly aligned together. Multi-line function
+// call takes precedence.
+if (array_key_exists(key, value)
+ && foo.bar.baz(
+ key, value2
+ )
+) {
+}
+
+if (true) {
+ foo = true;
+};
+
+if (foo == 401 // comment
+ || bar == 3200 /* long comment
+ here
+ */
+) {
+ return false;
+}
+
+if (foo == 401 // comment
+ || bar == 3200 // long comment here
+) {
+ return false;
+}
+
+if (IPP.errorCode() == 401
+ // Comment explaining the next condition here.
+ || IPP.errorCode() == 3200
+) {
+ return false;
+}
+
+function bar() {
+ if (a
+ && b
+ ) {
+ return false;
+ }
+}
+
+if (a
+ && foo(
+ 'a',
+ 'b'
+ )
+) {
+ return false;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the MultiLineCondition 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\Standards\PEAR\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class MultiLineConditionUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='MultiLineConditionUnitTest.inc')
+ {
+ $errors = array(
+ 21 => 1,
+ 22 => 1,
+ 35 => 1,
+ 40 => 1,
+ 41 => 1,
+ 42 => 1,
+ 43 => 1,
+ 49 => 1,
+ 54 => 1,
+ 57 => 1,
+ 58 => 1,
+ 59 => 1,
+ 61 => 1,
+ 67 => 1,
+ 87 => 1,
+ 88 => 1,
+ 89 => 1,
+ 90 => 1,
+ 96 => 2,
+ 101 => 1,
+ 109 => 2,
+ 125 => 1,
+ 145 => 2,
+ 153 => 2,
+ 168 => 1,
+ 177 => 1,
+ );
+
+ if ($testFile === 'MultiLineConditionUnitTest.inc') {
+ $errors[183] = 1;
+ }
+
+ return $errors;
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// These are statements and should not have brackets.
+require_once('blank.inc');
+require('blank.inc');
+
+$test = true;
+
+// Conditionally including a class file: use include_once
+if ($test) {
+ require_once 'blank.inc';
+ require 'blank.inc';
+}
+
+// Unconditionally including a class file: use require_once
+include_once 'blank.inc';
+include 'blank.inc';
+
+
+// These are ok
+if ($test) {
+ include_once 'blank.inc';
+ include 'blank.inc';
+}
+
+require_once 'blank.inc';
+require 'blank.inc';
+
+?>
+<pre>
+Some content goes here.
+<?php
+include_once 'blank.inc';
+include 'blank.inc';
+require_once 'blank.inc';
+require 'blank.inc';
+?>
+</pre>
+<?php
+
+if (include_once 'blank.inc') {
+ $var = include_once 'blank.inc';
+ if ($var === true) {
+ }
+}
+
+if (require_once 'blank.inc') {
+ $var = require_once 'blank.inc';
+ if ($var === true) {
+ }
+}
+
+function get_some_value()
+{
+ include_once 'blank.inc';
+ include 'blank.inc';
+
+ // These are ok
+ if ($test) {
+ include_once 'blank.inc';
+ include 'blank.inc';
+ }
+
+ require_once 'blank.inc';
+ require 'blank.inc';
+
+ ?>
+ <pre>
+ Some content goes here.
+ <?php
+ include_once 'blank.inc';
+ include 'blank.inc';
+ require_once 'blank.inc';
+ require 'blank.inc';
+ ?>
+ </pre>
+ <?php
+
+ if (include_once 'blank.inc') {
+ $var = include_once 'blank.inc';
+ if ($var === true) {
+ }
+ }
+
+ if (require_once 'blank.inc') {
+ $var = require_once 'blank.inc';
+ if ($var === true) {
+ }
+ }
+}
+
+
+$var = include_once 'blank.inc';
+if ($var == false) {
+
+}
+
+require_once ('blank.inc');
+include_once ('blank.inc');
--- /dev/null
+<?php
+
+// These are statements and should not have brackets.
+require_once 'blank.inc';
+require 'blank.inc';
+
+$test = true;
+
+// Conditionally including a class file: use include_once
+if ($test) {
+ include_once 'blank.inc';
+ include 'blank.inc';
+}
+
+// Unconditionally including a class file: use require_once
+require_once 'blank.inc';
+require 'blank.inc';
+
+
+// These are ok
+if ($test) {
+ include_once 'blank.inc';
+ include 'blank.inc';
+}
+
+require_once 'blank.inc';
+require 'blank.inc';
+
+?>
+<pre>
+Some content goes here.
+<?php
+require_once 'blank.inc';
+require 'blank.inc';
+require_once 'blank.inc';
+require 'blank.inc';
+?>
+</pre>
+<?php
+
+if (include_once 'blank.inc') {
+ $var = include_once 'blank.inc';
+ if ($var === true) {
+ }
+}
+
+if (include_once 'blank.inc') {
+ $var = include_once 'blank.inc';
+ if ($var === true) {
+ }
+}
+
+function get_some_value()
+{
+ include_once 'blank.inc';
+ include 'blank.inc';
+
+ // These are ok
+ if ($test) {
+ include_once 'blank.inc';
+ include 'blank.inc';
+ }
+
+ include_once 'blank.inc';
+ include 'blank.inc';
+
+ ?>
+ <pre>
+ Some content goes here.
+ <?php
+ include_once 'blank.inc';
+ include 'blank.inc';
+ include_once 'blank.inc';
+ include 'blank.inc';
+ ?>
+ </pre>
+ <?php
+
+ if (include_once 'blank.inc') {
+ $var = include_once 'blank.inc';
+ if ($var === true) {
+ }
+ }
+
+ if (include_once 'blank.inc') {
+ $var = include_once 'blank.inc';
+ if ($var === true) {
+ }
+ }
+}
+
+
+$var = include_once 'blank.inc';
+if ($var == false) {
+
+}
+
+require_once 'blank.inc';
+require_once 'blank.inc';
--- /dev/null
+<?php
+/**
+ * Unit test class for the IncludingFile 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\Standards\PEAR\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class IncludingFileUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 4 => 1,
+ 5 => 1,
+ 11 => 1,
+ 12 => 1,
+ 16 => 1,
+ 17 => 1,
+ 33 => 1,
+ 34 => 1,
+ 47 => 1,
+ 48 => 1,
+ 64 => 1,
+ 65 => 1,
+ 73 => 1,
+ 74 => 1,
+ 85 => 1,
+ 86 => 1,
+ 98 => 1,
+ 99 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$GLOBALS['TSFE']->additionalHeaderData[$this->strApplicationName]
+ = $this->xajax->getJavascript(t3lib_extMgm::siteRelPath('nr_xajax'));
+
+$GLOBALS['TSFE']->additionalHeaderData[$this->strApplicationName]
+ = $this->xajax->getJavascript(t3lib_extMgm::siteRelPath('nr_xajax'));
+
+$GLOBALS['TSFE']->additionalHeaderData[$this->strApplicationName] =
+ $this->xajax->getJavascript(t3lib_extMgm::siteRelPath('nr_xajax'));
+
+$GLOBALS['TSFE']->additionalHeaderData[$this->strApplicationName]
+ = $this->xajax->getJavascript(t3lib_extMgm::siteRelPath('nr_xajax'));
+$GLOBALS['TSFE']->additionalHeaderData[$this->strApplicationName] = 'boo'
+
+$var='string';
+
+function getInstalledStandards(
+ $includeGeneric=false,
+ $standardsDir=''
+) {
+}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the MultiLineAssignment 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\Standards\PEAR\Tests\Formatting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class MultiLineAssignmentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 6 => 1,
+ 8 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php test(
+);
+test();
+test($arg, $arg2);
+isset ();
+test( );
+test() ;
+test( $arg);
+empty( $arg );
+eval ( $arg );
+
+if (is_array($arg) === true) {
+
+}
+
+$something = get($arg1, $arg2);
+$something = get($arg1, $arg2) ;
+$something = get($arg1, $arg2) ;
+
+// No errors as this test only checks for function calls.
+class TestClass extends MyClass
+{
+
+ const const1 = 'hello';
+ const CONST2 = 'hello';
+
+ public function test () { }
+}
+
+make_foo($string/*the string*/, true/*test*/);
+make_foo($string/*the string*/, true/*test*/ );
+make_foo($string /*the string*/, true /*test*/);
+make_foo(/*the string*/$string, /*test*/true);
+make_foo( /*the string*/$string, /*test*/true);
+
+// No errors should be throw here because
+// this is multi-line.
+throw new Exception(
+ 'Exception text'
+);
+
+// Objects are the same as a normal call.
+$obj = new TestClass( );
+
+// Heredocs dont need to be indented.
+method_call(
+<<<EOH
+Anyone want to recomment parse errors?
+
+EOH
+);
+
+fputs(
+ STDOUT,
+ 'Examples:
+ $ {$app} --all
+ $ {$app} --all');
+
+fputs(STDOUT,
+ "Examples:
+ $ {$app} --all
+ $ {$app} --all",
+$something
+ );
+
+// This is not a function call.
+function &testFunction($arg1,
+ $arg2,
+) {
+}
+
+$array = array();
+array_map(
+ function($x)
+ {
+ return trim($x);
+ },
+ $array
+);
+
+array_map(
+ function($x)
+ {
+ return trim($x);
+ },
+ $array
+);
+
+$bar = new stdClass(
+ 4,
+ 5,
+ 6
+
+);
+
+$bar = new stdClass(
+ 4,
+ 5,
+ 6
+
+);
+
+$foo = new stdClass(
+ 1,
+ 2,
+ 3);
+
+public function doSomething()
+{
+ return $this->getFoo()
+ ->doBar(
+ $this->getX() // no comma here
+ ->doY() // this is still the first method argument
+ ->doZ() // this is still the first method argument
+ );
+}
+
+$var = myFunction(
+$foo,
+$bar
+);
+
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature allowMultipleArguments false
+
+fputs(
+ STDOUT,
+ "Examples:
+ $ {$app} , --all
+ $ {$app} --all", $something
+);
+
+$array = array();
+array_map(
+ function($x)
+ {
+ return trim($x, $y);
+ }, $foo,
+ $array
+);
+
+$bar = new stdClass(
+ 4, /* thanks */ 5, /* PSR-2 */ 6
+);
+
+public function doSomething()
+{
+ return $this->getFoo()
+ ->doBar(
+ $this->getX() // no comma here
+ ->doY() // this is still the first method argument
+ ->doZ() // this is still the first method argument
+ );
+}
+
+doError(
+ 404, // status code
+ 'Not Found', // error name
+ 'Check your id' // fix
+);
+
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature allowMultipleArguments true
+
+// Don't report errors for closing braces. Leave that to other sniffs.
+foo(
+ [
+ 'this',
+ 'is',
+ 'an',
+ 'array'
+ ],
+[
+ 'this',
+ 'is',
+ 'an',
+ 'array'
+ ],
+ array(
+ 'this',
+ 'is',
+'an',
+'array'
+ ),
+ array(
+ 'this',
+ 'is',
+ 'an',
+ 'array'
+ ),
+ function($x)
+ {
+ echo 'wee';
+
+ return trim($x);
+ }
+);
+
+function foo()
+{
+ myFunction(
+ 'string'.
+ // comment
+ // comment
+ 'string'.
+ /* comment
+ * comment
+ */
+ 'string'.
+ );
+}
+
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesAfterOpen 1
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesBeforeClose 1
+test($arg, $arg2);
+test( $arg, $arg2 );
+test( $arg, $arg2 );
+test();
+test( );
+test( );
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesAfterOpen 0
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesBeforeClose 0
+
+?>
+<script>
+ var foo = <?php echo bar(
+ 'baz'
+ ); ?>;
+</script>
+ <script>
+ var foo = <?php echo bar(
+ 'baz'
+ ); ?>;
+ </script>
+<?php
+
+array_walk(
+ $types,
+ function ($title, $type) {
+ $plural = 'all' !== $type ? 's' : '';
+ ?>
+ <li><a href="<?php esc_url('#tabs-documents-' . $type . $plural); ?>"><?php echo esc_html($title); ?></a></li>
+ <?php
+ }
+);
+
+$this->log(// ...
+ 'error',
+ sprintf(
+ 'Message: %s',
+ isset($e->getData()['object']['evidence_details'])
+ ? $e->getData()['object']['evidence_details']['due_by']
+ : ''
+ ),
+ array($e->getData()['object'])
+);
+
+?>
+<div>
+ <?php getTemplatePart(
+ 'partials/web-page/carousel-slick/item-slide/header',
+ [
+ 'class' => $class
+ ]
+ ); ?>
+</div>
+
+<?php
+if (true) {
+ $test = '
+ ' . function_0(
+ $argument_0
+ );
+}
+
+if (true) {
+ $test = '
+ ' . function_0(
+ $argument_0,
+ $argument_1
+ );
+}
+
+myFunction(
+ 'foo', (object) array(
+ 'bar' => function ($x) {
+ return true;
+ },
+ 'baz' => false
+ )
+);
+$qux = array_filter(
+ $quux, function ($x) {
+ return $x;
+ }
+);
+
+array_filter(
+ [1, 2],
+ function ($i) : bool {
+ return $i === 0;
+ }
+);
+
+foo(array(
+ 'callback' => function () {
+ $foo = 'foo';
+ return;
+ },
+));
+
+foo(
+ $a,
+ /*
+ $c,
+
+ $d,
+ */
+ $e
+);
+
+test(
+ 1,2,3,4
+ );
+
+class Test
+{
+ public function getInstance()
+ {
+ return new static(
+ 'arg',
+ 'foo'
+ );
+ }
+
+ public function getSelf()
+ {
+ return new self(
+ 'a','b', 'c'
+ );
+ }
+}
+
+$x = $var('y',
+'x');
+
+$obj->{$x}(1,
+ 2);
+
+(function ($a, $b) {
+ return function ($c, $d) use ($a, $b) {
+ echo $a, $b, $c, $d;
+ };
+})(
+ 'a','b'
+)('c',
+ 'd');
+
+class Foo
+{
+ public function bar($a, $b)
+ {
+ if (!$a || !$b) {
+ return;
+ }
+
+ (new stdClass())->a = $a;
+ }
+}
+
+(function ($a, $b) {
+ return function ($c, $d) use ($a, $b) {
+ echo $a, $b, $c, $d;
+ };
+})('a','b')('c','d');
--- /dev/null
+<?php test(
+);
+test();
+test($arg, $arg2);
+isset();
+test();
+test();
+test($arg);
+empty($arg);
+eval($arg);
+
+if (is_array($arg) === true) {
+
+}
+
+$something = get($arg1, $arg2);
+$something = get($arg1, $arg2);
+$something = get($arg1, $arg2);
+
+// No errors as this test only checks for function calls.
+class TestClass extends MyClass
+{
+
+ const const1 = 'hello';
+ const CONST2 = 'hello';
+
+ public function test () { }
+}
+
+make_foo($string/*the string*/, true/*test*/);
+make_foo($string/*the string*/, true/*test*/);
+make_foo($string /*the string*/, true /*test*/);
+make_foo(/*the string*/$string, /*test*/true);
+make_foo(/*the string*/$string, /*test*/true);
+
+// No errors should be throw here because
+// this is multi-line.
+throw new Exception(
+ 'Exception text'
+);
+
+// Objects are the same as a normal call.
+$obj = new TestClass();
+
+// Heredocs dont need to be indented.
+method_call(
+<<<EOH
+Anyone want to recomment parse errors?
+
+EOH
+);
+
+fputs(
+ STDOUT,
+ 'Examples:
+ $ {$app} --all
+ $ {$app} --all'
+);
+
+fputs(
+ STDOUT,
+ "Examples:
+ $ {$app} --all
+ $ {$app} --all",
+ $something
+);
+
+// This is not a function call.
+function &testFunction($arg1,
+ $arg2,
+) {
+}
+
+$array = array();
+array_map(
+ function($x)
+ {
+ return trim($x);
+ },
+ $array
+);
+
+array_map(
+ function($x)
+ {
+ return trim($x);
+ },
+ $array
+);
+
+$bar = new stdClass(
+ 4,
+ 5,
+ 6
+);
+
+$bar = new stdClass(
+ 4,
+ 5,
+ 6
+);
+
+$foo = new stdClass(
+ 1,
+ 2,
+ 3
+);
+
+public function doSomething()
+{
+ return $this->getFoo()
+ ->doBar(
+ $this->getX() // no comma here
+ ->doY() // this is still the first method argument
+ ->doZ() // this is still the first method argument
+ );
+}
+
+$var = myFunction(
+ $foo,
+ $bar
+);
+
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature allowMultipleArguments false
+
+fputs(
+ STDOUT,
+ "Examples:
+ $ {$app} , --all
+ $ {$app} --all",
+ $something
+);
+
+$array = array();
+array_map(
+ function($x)
+ {
+ return trim($x, $y);
+ },
+ $foo,
+ $array
+);
+
+$bar = new stdClass(
+ 4, /* thanks */
+ 5, /* PSR-2 */
+ 6
+);
+
+public function doSomething()
+{
+ return $this->getFoo()
+ ->doBar(
+ $this->getX() // no comma here
+ ->doY() // this is still the first method argument
+ ->doZ() // this is still the first method argument
+ );
+}
+
+doError(
+ 404, // status code
+ 'Not Found', // error name
+ 'Check your id' // fix
+);
+
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature allowMultipleArguments true
+
+// Don't report errors for closing braces. Leave that to other sniffs.
+foo(
+ [
+ 'this',
+ 'is',
+ 'an',
+ 'array'
+ ],
+ [
+ 'this',
+ 'is',
+ 'an',
+ 'array'
+ ],
+ array(
+ 'this',
+ 'is',
+ 'an',
+ 'array'
+ ),
+ array(
+ 'this',
+ 'is',
+ 'an',
+ 'array'
+ ),
+ function($x)
+ {
+ echo 'wee';
+
+ return trim($x);
+ }
+);
+
+function foo()
+{
+ myFunction(
+ 'string'.
+ // comment
+ // comment
+ 'string'.
+ /* comment
+ * comment
+ */
+ 'string'.
+ );
+}
+
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesAfterOpen 1
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesBeforeClose 1
+test( $arg, $arg2 );
+test( $arg, $arg2 );
+test( $arg, $arg2 );
+test();
+test();
+test();
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesAfterOpen 0
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesBeforeClose 0
+
+?>
+<script>
+ var foo = <?php echo bar(
+ 'baz'
+ ); ?>;
+</script>
+ <script>
+ var foo = <?php echo bar(
+ 'baz'
+ ); ?>;
+ </script>
+<?php
+
+array_walk(
+ $types,
+ function ($title, $type) {
+ $plural = 'all' !== $type ? 's' : '';
+ ?>
+ <li><a href="<?php esc_url('#tabs-documents-' . $type . $plural); ?>"><?php echo esc_html($title); ?></a></li>
+ <?php
+ }
+);
+
+$this->log(// ...
+ 'error',
+ sprintf(
+ 'Message: %s',
+ isset($e->getData()['object']['evidence_details'])
+ ? $e->getData()['object']['evidence_details']['due_by']
+ : ''
+ ),
+ array($e->getData()['object'])
+);
+
+?>
+<div>
+ <?php getTemplatePart(
+ 'partials/web-page/carousel-slick/item-slide/header',
+ [
+ 'class' => $class
+ ]
+ ); ?>
+</div>
+
+<?php
+if (true) {
+ $test = '
+ ' . function_0(
+ $argument_0
+ );
+}
+
+if (true) {
+ $test = '
+ ' . function_0(
+ $argument_0,
+ $argument_1
+ );
+}
+
+myFunction(
+ 'foo', (object) array(
+ 'bar' => function ($x) {
+ return true;
+ },
+ 'baz' => false
+ )
+);
+$qux = array_filter(
+ $quux, function ($x) {
+ return $x;
+ }
+);
+
+array_filter(
+ [1, 2],
+ function ($i) : bool {
+ return $i === 0;
+ }
+);
+
+foo(
+ array(
+ 'callback' => function () {
+ $foo = 'foo';
+ return;
+ },
+ )
+);
+
+foo(
+ $a,
+ /*
+ $c,
+
+ $d,
+ */
+ $e
+);
+
+test(
+ 1,2,3,4
+);
+
+class Test
+{
+ public function getInstance()
+ {
+ return new static(
+ 'arg',
+ 'foo'
+ );
+ }
+
+ public function getSelf()
+ {
+ return new self(
+ 'a','b', 'c'
+ );
+ }
+}
+
+$x = $var(
+ 'y',
+ 'x'
+);
+
+$obj->{$x}(
+ 1,
+ 2
+);
+
+(function ($a, $b) {
+ return function ($c, $d) use ($a, $b) {
+ echo $a, $b, $c, $d;
+ };
+})(
+ 'a','b'
+)(
+ 'c',
+ 'd'
+);
+
+class Foo
+{
+ public function bar($a, $b)
+ {
+ if (!$a || !$b) {
+ return;
+ }
+
+ (new stdClass())->a = $a;
+ }
+}
+
+(function ($a, $b) {
+ return function ($c, $d) use ($a, $b) {
+ echo $a, $b, $c, $d;
+ };
+})('a','b')('c','d');
--- /dev/null
+test(
+);
+test();
+test(arg, arg2);
+test ();
+test( );
+test() ;
+test( arg);
+test( arg );
+test ( arg );
+
+if (foo(arg) === true) {
+
+}
+
+var something = get(arg1, arg2);
+var something = get(arg1, arg2) ;
+var something = get(arg1, arg2) ;
+
+make_foo(string/*the string*/, true/*test*/);
+make_foo(string/*the string*/, true/*test*/ );
+make_foo(string /*the string*/, true /*test*/);
+make_foo(/*the string*/string, /*test*/true);
+make_foo( /*the string*/string, /*test*/true);
+
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesAfterOpen 1
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesBeforeClose 1
+test(arg, arg2);
+test( arg, arg2 );
+test( arg, arg2 );
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesAfterOpen 0
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesBeforeClose 0
+
+this.init = function(data) {
+ a.b('').a(function(itemid, target) {
+ b(
+ itemid,
+ target,
+ {
+ reviewData: _reviewData,
+ pageid: itemid
+ },
+ '',
+ function() {
+ var _showAspectItems = function(itemid) {
+ a.a(a.c(''), '');
+ a.b(a.c('-' + itemid), '');
+ };
+ a.foo(function(itemid, target) {
+ _foo(itemid);
+ });
+ }
+ );
+ });
+};
+
+a.prototype = {
+
+ a: function()
+ {
+ this.addItem(
+ {
+ /**
+ * @return void
+ */
+ a: function()
+ {
+
+ },
+ /**
+ * @return void
+ */
+ a: function()
+ {
+
+ },
+ }
+ );
+ }
+};
--- /dev/null
+test(
+);
+test();
+test(arg, arg2);
+test();
+test();
+test();
+test(arg);
+test(arg);
+test(arg);
+
+if (foo(arg) === true) {
+
+}
+
+var something = get(arg1, arg2);
+var something = get(arg1, arg2);
+var something = get(arg1, arg2);
+
+make_foo(string/*the string*/, true/*test*/);
+make_foo(string/*the string*/, true/*test*/);
+make_foo(string /*the string*/, true /*test*/);
+make_foo(/*the string*/string, /*test*/true);
+make_foo(/*the string*/string, /*test*/true);
+
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesAfterOpen 1
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesBeforeClose 1
+test( arg, arg2 );
+test( arg, arg2 );
+test( arg, arg2 );
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesAfterOpen 0
+// @codingStandardsChangeSetting PEAR.Functions.FunctionCallSignature requiredSpacesBeforeClose 0
+
+this.init = function(data) {
+ a.b('').a(
+ function(itemid, target) {
+ b(
+ itemid,
+ target,
+ {
+ reviewData: _reviewData,
+ pageid: itemid
+ },
+ '',
+ function() {
+ var _showAspectItems = function(itemid) {
+ a.a(a.c(''), '');
+ a.b(a.c('-' + itemid), '');
+ };
+ a.foo(
+ function(itemid, target) {
+ _foo(itemid);
+ }
+ );
+ }
+ );
+ }
+ );
+};
+
+a.prototype = {
+
+ a: function()
+ {
+ this.addItem(
+ {
+ /**
+ * @return void
+ */
+ a: function()
+ {
+
+ },
+ /**
+ * @return void
+ */
+ a: function()
+ {
+
+ },
+ }
+ );
+ }
+};
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionCallSignature 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\Standards\PEAR\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionCallSignatureUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='FunctionCallSignatureUnitTest.inc')
+ {
+ if ($testFile === 'FunctionCallSignatureUnitTest.js') {
+ return array(
+ 5 => 1,
+ 6 => 2,
+ 7 => 1,
+ 8 => 1,
+ 9 => 2,
+ 10 => 3,
+ 17 => 1,
+ 18 => 1,
+ 21 => 1,
+ 24 => 1,
+ 28 => 2,
+ 30 => 2,
+ 35 => 1,
+ 49 => 1,
+ 51 => 1,
+ 54 => 1,
+ 70 => 1,
+ 71 => 1,
+ );
+ }//end if
+
+ return array(
+ 5 => 1,
+ 6 => 2,
+ 7 => 1,
+ 8 => 1,
+ 9 => 2,
+ 10 => 3,
+ 17 => 1,
+ 18 => 1,
+ 31 => 1,
+ 34 => 1,
+ 43 => 2,
+ 57 => 1,
+ 59 => 1,
+ 63 => 1,
+ 64 => 1,
+ 82 => 1,
+ 93 => 1,
+ 100 => 1,
+ 106 => 2,
+ 119 => 1,
+ 120 => 1,
+ 129 => 1,
+ 137 => 1,
+ 142 => 2,
+ 171 => 1,
+ 180 => 1,
+ 181 => 1,
+ 194 => 1,
+ 213 => 2,
+ 215 => 2,
+ 217 => 2,
+ 218 => 2,
+ 277 => 1,
+ 278 => 1,
+ 303 => 1,
+ 308 => 1,
+ 321 => 1,
+ 322 => 1,
+ 329 => 1,
+ 330 => 1,
+ 337 => 1,
+ 342 => 1,
+ 343 => 1,
+ 345 => 1,
+ 346 => 2,
+ 353 => 1,
+ 354 => 1,
+ 355 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+public function someFunctionWithAVeryLongName($firstParameter='something',
+ $secondParameter='booooo', $third=null, $fourthParameter=false,
+ $fifthParameter=123.12, $sixthParam=true
+){
+}
+
+function someFunctionWithAVeryLongName2($firstParameter='something',
+$secondParameter='booooo', $third=null, $fourthParameter=false,
+$fifthParameter=123.12, $sixthParam=true
+) {
+}
+
+function blah() {
+}
+
+function blah()
+{
+}
+
+class MyClass
+{
+
+ public function someFunctionWithAVeryLongName($firstParameter='something',
+ $secondParameter='booooo', $third=null, $fourthParameter=false,
+ $fifthParameter=123.12, $sixthParam=true
+ ) /** w00t */ {
+ }
+
+ public function someFunctionWithAVeryLongName2(
+ $firstParameter='something', $secondParameter='booooo', $third=null
+ ) {
+ }
+
+ protected abstract function processTokenWithinScope(
+ PHP_CodeSniffer_File $phpcsFile,
+ $stackPtr,
+ $currScope
+ );
+
+ protected abstract function processToken(
+ PHP_CodeSniffer_File $phpcsFile,
+ $stackPtr,
+ $currScope);
+
+}
+
+function getInstalledStandards(
+ $includeGeneric=false,
+ $standardsDir=''
+)
+{
+}
+
+function &testFunction($arg1,
+ $arg2,
+) {
+}
+
+function testFunction($arg1,
+$arg2) {
+}
+
+function validateUrl(
+ $url,
+ $requireScheme=TRUE,
+ array $allowedSchemes=array(
+ 'http',
+ 'https',
+ ),
+ array $notAllowedSchemes=array('ftp', 'sftp')
+) {
+}
+
+function validateUrlShort(
+ $url,
+ $requireScheme=TRUE,
+ array $allowedSchemes=[
+ 'http',
+ 'https',
+ ],
+ array $notAllowedSchemes=['ftp', 'sftp']
+) {
+}
+
+$noArgs_longVars = function () use (
+ $longVar1,
+ $longerVar2,
+ $muchLongerVar3
+) {
+ // body
+};
+
+$longArgs_longVars = function (
+ $longArgument,
+ $longerArgument,
+ $muchLongerArgument
+) use (
+ $longVar1,
+ $longerVar2,
+ $muchLongerVar3
+) {
+ // body
+};
+
+$longArgs_longVars = function (
+ $longArgument,
+ $longerArgument,
+ $muchLongerArgument
+ ) use (
+ $longVar1,
+ $longerVar2,
+ $muchLongerVar3
+) {
+ // body
+};
+
+$longArgs_longVars = function (
+ $longArgument,
+ $muchLongerArgument)use(
+ $muchLongerVar3) {
+ // body
+};
+
+function test()
+{
+ $longArgs_longVars = function (
+ $longArgument,
+ $longerArgument,
+ $muchLongerArgument
+ ) use (
+ $longVar1,
+ $longerVar2,
+ $muchLongerVar3
+ ) {
+ // body
+ };
+}
+
+function
+ myFunction()
+{
+}
+
+function
+myFunction()
+{
+}
+
+
+use function foo\bar;
+
+use
+ function bar\baz;
+
+namespace {
+ use function Name\Space\f;
+ f();
+}
+
+$var = function() {return true;};
+$var = function() {return true;
+};
+function blah(){return true;
+}
+
+$closureWithArgsAndVars = function($arg1, $arg2) use ($var1, $var2){
+ // body
+};
+
+function
+blah
+()
+{
+ // body
+}
+
+$b = function &() {
+ echo "hello";
+};
+
+function foo(
+ $param1,
+ $param2,
+ $param3,
+) : SomeClass {
+}
+
+function foo(
+ $param1,
+ $param2,
+ $param3,
+): SomeClass {
+}
+
+function foo(
+ $param1,
+ $param2,
+ $param3,
+): SomeClass // Comment here
+{
+}
+
+function foo(
+ $param1,
+
+ $param2,
+
+ $param3,
+) : SomeClass {
+}
+
+function foo(
+ $var
+)
+{
+ // body
+}
+
+function foo(
+ $var
+)
+/* hello */ {
+ // body
+}
+
+function foo(
+ $var
+)
+{ echo 'hi';
+ // body
+}
+
+function foo(
+ $var
+)
+/* hello */ { echo 'hi';
+ // body
+}
--- /dev/null
+<?php
+public function someFunctionWithAVeryLongName($firstParameter='something',
+ $secondParameter='booooo', $third=null, $fourthParameter=false,
+ $fifthParameter=123.12, $sixthParam=true
+) {
+}
+
+function someFunctionWithAVeryLongName2($firstParameter='something',
+ $secondParameter='booooo', $third=null, $fourthParameter=false,
+ $fifthParameter=123.12, $sixthParam=true
+) {
+}
+
+function blah()
+{
+}
+
+function blah()
+{
+}
+
+class MyClass
+{
+
+ public function someFunctionWithAVeryLongName($firstParameter='something',
+ $secondParameter='booooo', $third=null, $fourthParameter=false,
+ $fifthParameter=123.12, $sixthParam=true
+ ) /** w00t */ {
+ }
+
+ public function someFunctionWithAVeryLongName2(
+ $firstParameter='something', $secondParameter='booooo', $third=null
+ ) {
+ }
+
+ protected abstract function processTokenWithinScope(
+ PHP_CodeSniffer_File $phpcsFile,
+ $stackPtr,
+ $currScope
+ );
+
+ protected abstract function processToken(
+ PHP_CodeSniffer_File $phpcsFile,
+ $stackPtr,
+ $currScope
+);
+
+}
+
+function getInstalledStandards(
+ $includeGeneric=false,
+ $standardsDir=''
+) {
+}
+
+function &testFunction($arg1,
+ $arg2,
+) {
+}
+
+function testFunction($arg1,
+ $arg2
+) {
+}
+
+function validateUrl(
+ $url,
+ $requireScheme=TRUE,
+ array $allowedSchemes=array(
+ 'http',
+ 'https',
+ ),
+ array $notAllowedSchemes=array('ftp', 'sftp')
+) {
+}
+
+function validateUrlShort(
+ $url,
+ $requireScheme=TRUE,
+ array $allowedSchemes=[
+ 'http',
+ 'https',
+ ],
+ array $notAllowedSchemes=['ftp', 'sftp']
+) {
+}
+
+$noArgs_longVars = function () use (
+ $longVar1,
+ $longerVar2,
+ $muchLongerVar3
+) {
+ // body
+};
+
+$longArgs_longVars = function (
+ $longArgument,
+ $longerArgument,
+ $muchLongerArgument
+) use (
+ $longVar1,
+ $longerVar2,
+ $muchLongerVar3
+) {
+ // body
+};
+
+$longArgs_longVars = function (
+ $longArgument,
+ $longerArgument,
+ $muchLongerArgument
+) use (
+ $longVar1,
+ $longerVar2,
+ $muchLongerVar3
+) {
+ // body
+};
+
+$longArgs_longVars = function (
+ $longArgument,
+ $muchLongerArgument
+) use (
+ $muchLongerVar3
+) {
+ // body
+};
+
+function test()
+{
+ $longArgs_longVars = function (
+ $longArgument,
+ $longerArgument,
+ $muchLongerArgument
+ ) use (
+ $longVar1,
+ $longerVar2,
+ $muchLongerVar3
+ ) {
+ // body
+ };
+}
+
+function myFunction()
+{
+}
+
+function myFunction()
+{
+}
+
+
+use function foo\bar;
+
+use
+ function bar\baz;
+
+namespace {
+ use function Name\Space\f;
+ f();
+}
+
+$var = function () {
+return true;};
+$var = function () {
+return true;
+};
+function blah()
+{
+return true;
+}
+
+$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
+ // body
+};
+
+function blah()
+{
+ // body
+}
+
+$b = function &() {
+ echo "hello";
+};
+
+function foo(
+ $param1,
+ $param2,
+ $param3,
+) : SomeClass {
+}
+
+function foo(
+ $param1,
+ $param2,
+ $param3,
+): SomeClass {
+}
+
+function foo(
+ $param1,
+ $param2,
+ $param3,
+): SomeClass { // Comment here
+}
+
+function foo(
+ $param1,
+ $param2,
+ $param3,
+) : SomeClass {
+}
+
+function foo(
+ $var
+) {
+ // body
+}
+
+function foo(
+ $var
+) {
+/* hello */
+ // body
+}
+
+function foo(
+ $var
+) {
+echo 'hi';
+ // body
+}
+
+function foo(
+ $var
+) {
+/* hello */ echo 'hi';
+ // body
+}
--- /dev/null
+
+function someFunctionWithAVeryLongName(firstParameter='something',
+ secondParameter='booooo', third=null, fourthParameter=false,
+ fifthParameter=123.12, sixthParam=true
+){
+}
+
+function someFunctionWithAVeryLongName2(firstParameter='something',
+secondParameter='booooo', third=null, fourthParameter=false,
+fifthParameter=123.12, sixthParam=true
+) {
+}
+
+function blah() {
+}
+
+function blah()
+{
+}
+
+var object =
+{
+
+ someFunctionWithAVeryLongName: function (firstParameter='something',
+ secondParameter='booooo', third=null, fourthParameter=false,
+ fifthParameter=123.12, sixthParam=true
+ ) /** w00t */ {
+ }
+
+ someFunctionWithAVeryLongName2: function (
+ firstParameter='something', secondParameter='booooo', third=null
+ ) {
+ }
+
+}
+
+function getInstalledStandards(
+ includeGeneric=false,
+ standardsDir=''
+)
+{
+}
+
+var a = Function('return 1+1');
+
+class test
+{
+ myFunction() {
+ return false;
+ }
+
+ myFunction2()
+ {
+ return false;
+ }
+}
--- /dev/null
+
+function someFunctionWithAVeryLongName(firstParameter='something',
+ secondParameter='booooo', third=null, fourthParameter=false,
+ fifthParameter=123.12, sixthParam=true
+) {
+}
+
+function someFunctionWithAVeryLongName2(firstParameter='something',
+ secondParameter='booooo', third=null, fourthParameter=false,
+ fifthParameter=123.12, sixthParam=true
+) {
+}
+
+function blah()
+{
+}
+
+function blah()
+{
+}
+
+var object =
+{
+
+ someFunctionWithAVeryLongName: function (firstParameter='something',
+ secondParameter='booooo', third=null, fourthParameter=false,
+ fifthParameter=123.12, sixthParam=true
+ ) /** w00t */ {
+ }
+
+ someFunctionWithAVeryLongName2: function (
+ firstParameter='something', secondParameter='booooo', third=null
+ ) {
+ }
+
+}
+
+function getInstalledStandards(
+ includeGeneric=false,
+ standardsDir=''
+) {
+}
+
+var a = Function('return 1+1');
+
+class test
+{
+ myFunction()
+ {
+ return false;
+ }
+
+ myFunction2()
+ {
+ return false;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionDeclaration 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\Standards\PEAR\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='FunctionDeclarationUnitTest.inc')
+ {
+ if ($testFile === 'FunctionDeclarationUnitTest.inc') {
+ $errors = array(
+ 3 => 1,
+ 4 => 1,
+ 5 => 1,
+ 9 => 1,
+ 10 => 1,
+ 11 => 1,
+ 14 => 1,
+ 17 => 1,
+ 44 => 1,
+ 52 => 1,
+ 61 => 2,
+ 98 => 1,
+ 110 => 2,
+ 120 => 3,
+ 121 => 1,
+ 140 => 1,
+ 145 => 1,
+ 161 => 2,
+ 162 => 2,
+ 164 => 2,
+ 167 => 2,
+ 171 => 1,
+ 173 => 1,
+ 201 => 1,
+ 206 => 1,
+ 208 => 1,
+ 216 => 1,
+ 223 => 1,
+ 230 => 1,
+ 237 => 1,
+ );
+ } else {
+ $errors = array(
+ 3 => 1,
+ 4 => 1,
+ 5 => 1,
+ 9 => 1,
+ 10 => 1,
+ 11 => 1,
+ 14 => 1,
+ 17 => 1,
+ 41 => 1,
+ 48 => 1,
+ );
+ }//end if
+
+ return $errors;
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// No args.
+function myFunction()
+{
+}
+
+// No default args.
+function myFunction($arg1)
+{
+}
+
+// Valid
+function myFunction($arg1, $arg2='hello')
+{
+}
+
+// Valid with lots of args
+function myFunction($arg1, $arg2, $arg3, $arg4='hello', $arg5=array(), $arg6='hello')
+{
+}
+
+// Valid type hints
+function myFunction(array $arg1, array $arg2=array())
+{
+}
+
+// Invalid
+function myFunction($arg2='hello', $arg1)
+{
+}
+
+// Invalid with lots of args
+function myFunction($arg1, $arg2, $arg3, $arg4='hello', $arg5, $arg6='hello')
+{
+}
+
+// Invalid type hints
+function myFunction(array $arg2=array(), array $arg1)
+{
+}
+
+class myClass()
+{
+ // No args.
+ function myFunction()
+ {
+ }
+
+ // No default args.
+ function myFunction($arg1)
+ {
+ }
+
+ // Valid
+ function myFunction($arg1, $arg2='hello')
+ {
+ }
+
+ // Valid with lots of args
+ function myFunction($arg1, $arg2, $arg3, $arg4='hello', $arg5=array(), $arg6='hello')
+ {
+ }
+
+ // Valid type hints
+ function myFunction(array $arg1, array $arg2=array())
+ {
+ }
+
+ // Invalid
+ function myFunction($arg2='hello', $arg1)
+ {
+ }
+
+ // Invalid with lots of args
+ function myFunction($arg1, $arg2, $arg3, $arg4='hello', $arg5, $arg6='hello')
+ {
+ }
+
+ // Invalid type hints
+ function myFunction(array $arg2=array(), array $arg1)
+ {
+ }
+}
+
+function myFunc($req, $opt=null, ...$params) {}
+
+// Type hinting with NULL
+function foo(Foo $foo = null, $bar) {}
+function foo(Foo $foo, $bar) {}
+function foo(Foo $foo = null, $bar = true, $baz) {}
+function foo($baz, Foo $foo = null, $bar = true) {}
+function foo($baz, $bar = true, Foo $foo = null) {}
+
+// Valid closure
+function ($arg1, $arg2='hello') {}
+
+// Invalid closure
+function(array $arg2=array(), array $arg1) {}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ValidDefaultValue 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\Standards\PEAR\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ValidDefaultValueUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 29 => 1,
+ 34 => 1,
+ 39 => 1,
+ 71 => 1,
+ 76 => 1,
+ 81 => 1,
+ 91 => 1,
+ 99 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+class Valid_Name {}
+
+class invalid_Name {}
+
+class invalid_name {}
+
+class Invalid_name {}
+
+class VALID_Name {}
+
+class VALID_NAME {}
+
+class VALID_Name {}
+
+class ValidName {}
+
+class _Invalid_Name {}
+
+
+interface Valid_Name {}
+
+interface invalid_Name {}
+
+interface invalid_name {}
+
+interface Invalid_name {}
+
+interface VALID_Name {}
+
+interface VALID_NAME {}
+
+interface VALID_Name {}
+
+interface ValidName {}
+
+interface _Invalid_Name {}
+
+class ___ {}
+
+interface ___ {}
+
+class Invalid__Name {}
+
+interface Invalid__Name {}
+
+trait Valid_Name {}
+
+trait invalid_Name {}
+
+trait invalid_name {}
+
+trait Invalid_name {}
+
+trait VALID_Name {}
+
+trait VALID_NAME {}
+
+trait VALID_Name {}
+
+trait ValidName {}
+
+trait _Invalid_Name {}
+
+trait ___ {}
+
+trait Invalid__Name {}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ValidClassName 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\Standards\PEAR\Tests\NamingConventions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ValidClassNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 5 => 1,
+ 7 => 2,
+ 9 => 1,
+ 19 => 1,
+ 24 => 1,
+ 26 => 2,
+ 28 => 1,
+ 38 => 1,
+ 40 => 2,
+ 42 => 2,
+ 44 => 1,
+ 46 => 1,
+ 50 => 1,
+ 52 => 2,
+ 54 => 1,
+ 64 => 1,
+ 66 => 2,
+ 68 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+abstract class My_Class {
+
+ public function __construct() {}
+ public function My_Class() {}
+ public function _My_Class() {}
+
+ public function getSomeValue() {}
+ public function parseMyDSN() {}
+ public function get_some_value() {}
+ public function GetSomeValue() {}
+ public function getSomeValue_Again() {}
+ public function My_Package_getSomeValue() {}
+ public function _getSomeValue() {}
+ public function _parseMyDSN() {}
+ public function _get_some_value() {}
+ public function _GetSomeValue() {}
+ public function _getSomeValue_Again() {}
+ public function _My_Package_getSomeValue() {}
+
+ protected function getSomeValue() {}
+ protected function parseMyDSN() {}
+ protected function get_some_value() {}
+ protected function GetSomeValue() {}
+ protected function getSomeValue_Again() {}
+ protected function My_Package_getSomeValue() {}
+ protected function _getSomeValue() {}
+ protected function _parseMyDSN() {}
+ protected function _get_some_value() {}
+ protected function _GetSomeValue() {}
+ protected function _getSomeValue_Again() {}
+ protected function _My_Package_getSomeValue() {}
+
+ private function getSomeValue() {}
+ private function parseMyDSN() {}
+ private function get_some_value() {}
+ private function GetSomeValue() {}
+ private function getSomeValue_Again() {}
+ private function My_Package_getSomeValue() {}
+ private function _getSomeValue() {}
+ private function _parseMyDSN() {}
+ private function _get_some_value() {}
+ private function _GetSomeValue() {}
+ private function _getSomeValue_Again() {}
+ private function _My_Package_getSomeValue() {}
+
+ function getSomeValue() {}
+ function parseMyDSN() {}
+ function get_some_value() {}
+ function GetSomeValue() {}
+ function getSomeValue_Again() {}
+ function My_Package_getSomeValue() {}
+ function _getSomeValue() {}
+ function _parseMyDSN() {}
+ function _get_some_value() {}
+ function _GetSomeValue() {}
+ function _getSomeValue_Again() {}
+ function _My_Package_getSomeValue() {}
+
+}//end class
+
+interface My_Interface {
+
+ public function getSomeValue() {}
+ public function parseMyDSN() {}
+ public function get_some_value() {}
+ public function GetSomeValue() {}
+ public function getSomeValue_Again() {}
+ public function My_Package_getSomeValue() {}
+ public function _getSomeValue() {}
+ public function _parseMyDSN() {}
+ public function _get_some_value() {}
+ public function _GetSomeValue() {}
+ public function _getSomeValue_Again() {}
+ public function _My_Package_getSomeValue() {}
+
+ protected function getSomeValue() {}
+ protected function parseMyDSN() {}
+ protected function get_some_value() {}
+ protected function GetSomeValue() {}
+ protected function getSomeValue_Again() {}
+ protected function My_Package_getSomeValue() {}
+ protected function _getSomeValue() {}
+ protected function _parseMyDSN() {}
+ protected function _get_some_value() {}
+ protected function _GetSomeValue() {}
+ protected function _getSomeValue_Again() {}
+ protected function _My_Package_getSomeValue() {}
+
+ private function getSomeValue() {}
+ private function parseMyDSN() {}
+ private function get_some_value() {}
+ private function GetSomeValue() {}
+ private function getSomeValue_Again() {}
+ private function My_Package_getSomeValue() {}
+ private function _getSomeValue() {}
+ private function _parseMyDSN() {}
+ private function _get_some_value() {}
+ private function _GetSomeValue() {}
+ private function _getSomeValue_Again() {}
+ private function _My_Package_getSomeValue() {}
+
+ function getSomeValue() {}
+ function parseMyDSN() {}
+ function get_some_value() {}
+ function GetSomeValue() {}
+ function getSomeValue_Again() {}
+ function My_Package_getSomeValue() {}
+ function _getSomeValue() {}
+ function _parseMyDSN() {}
+ function _get_some_value() {}
+ function _GetSomeValue() {}
+ function _getSomeValue_Again() {}
+ function _My_Package_getSomeValue() {}
+
+}//end interface
+
+function My_Package_getSomeValue() {}
+function My_Package_parseMyDSN() {}
+function My_Package_get_some_value() {}
+function My_PackagegetSomeValue() {}
+function My_Package_getSomeValue_Again() {}
+function My_Package() {}
+function _My_Package_getSomeValue() {}
+function _My_Package_parseMyDSN() {}
+function _My_Package_get_some_value() {}
+function _My_PackagegetSomeValue() {}
+function _My_Package_getSomeValue_Again() {}
+function _My_Package() {}
+
+
+/* Test for magic functions */
+
+class Magic_Test {
+ function __construct() {}
+ function __destruct() {}
+ function __call() {}
+ function __callStatic() {}
+ function __get() {}
+ function __set() {}
+ function __isset() {}
+ function __unset() {}
+ function __sleep() {}
+ function __wakeup() {}
+ function __toString() {}
+ function __set_state() {}
+ function __clone() {}
+ function __autoload() {}
+ function __invoke() {}
+ function __myFunction() {}
+ function __my_function() {}
+}
+
+function __construct() {}
+function __destruct() {}
+function __call() {}
+function __callStatic() {}
+function __get() {}
+function __set() {}
+function __isset() {}
+function __unset() {}
+function __sleep() {}
+function __wakeup() {}
+function __toString() {}
+function __set_state() {}
+function __clone() {}
+function __autoload() {}
+function __invoke() {}
+function __myFunction() {}
+function __my_function() {}
+
+function my_package_function() {}
+function Package_() {}
+function Package() {}
+
+class Closure_Test {
+ function test() {
+ $foo = function() { echo 'foo'; };
+ }
+}
+
+function test() {
+ $foo = function() { echo 'foo'; };
+}
+
+/* @codingStandardsIgnoreStart */
+class MyClass
+{
+ /* @codingStandardsIgnoreEnd */
+ public function __construct() {}
+}
+
+trait Foo
+{
+ function __call() {}
+}
+
+class Magic_Case_Test {
+ function __Construct() {}
+ function __isSet() {}
+ function __tostring() {}
+}
+function __autoLoad() {}
+function _() {}
+
+function __debugInfo() {}
+class Foo {
+ function __debugInfo() {}
+}
+
+/* Magic methods in anonymous classes. */
+$a new class {
+ function __construct() {}
+ function __destruct() {}
+ function __call() {}
+ function __callStatic() {}
+ function __get() {}
+ function __set() {}
+ function __isset() {}
+ function __unset() {}
+ function __sleep() {}
+ function __wakeup() {}
+ function __toString() {}
+ function __set_state() {}
+ function __clone() {}
+ function __autoload() {}
+ function __invoke() {}
+ function __myFunction() {}
+ function __my_function() {}
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ValidFunctionName 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\Standards\PEAR\Tests\NamingConventions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ValidFunctionNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 11 => 1,
+ 12 => 1,
+ 13 => 1,
+ 14 => 1,
+ 15 => 1,
+ 16 => 1,
+ 17 => 1,
+ 18 => 1,
+ 19 => 1,
+ 20 => 1,
+ 24 => 1,
+ 25 => 1,
+ 26 => 1,
+ 27 => 1,
+ 28 => 1,
+ 29 => 1,
+ 30 => 1,
+ 31 => 1,
+ 32 => 1,
+ 33 => 1,
+ 35 => 1,
+ 36 => 1,
+ 37 => 1,
+ 38 => 1,
+ 39 => 1,
+ 40 => 1,
+ 43 => 1,
+ 44 => 1,
+ 45 => 1,
+ 46 => 1,
+ 50 => 1,
+ 51 => 1,
+ 52 => 1,
+ 53 => 1,
+ 56 => 1,
+ 57 => 1,
+ 58 => 1,
+ 59 => 1,
+ 67 => 1,
+ 68 => 1,
+ 69 => 1,
+ 70 => 1,
+ 71 => 1,
+ 72 => 1,
+ 73 => 1,
+ 74 => 1,
+ 75 => 1,
+ 76 => 1,
+ 80 => 1,
+ 81 => 1,
+ 82 => 1,
+ 83 => 1,
+ 84 => 1,
+ 85 => 1,
+ 86 => 1,
+ 87 => 1,
+ 88 => 1,
+ 89 => 1,
+ 91 => 1,
+ 92 => 1,
+ 93 => 1,
+ 94 => 1,
+ 95 => 1,
+ 96 => 1,
+ 99 => 1,
+ 100 => 1,
+ 101 => 1,
+ 102 => 1,
+ 106 => 1,
+ 107 => 1,
+ 108 => 1,
+ 109 => 1,
+ 112 => 1,
+ 113 => 1,
+ 114 => 1,
+ 115 => 1,
+ 121 => 1,
+ 122 => 1,
+ 123 => 1,
+ 124 => 1,
+ 125 => 1,
+ 126 => 1,
+ 127 => 1,
+ 128 => 1,
+ 129 => 1,
+ 130 => 1,
+ 149 => 1,
+ 151 => 1,
+ 152 => 1,
+ 155 => 1,
+ 156 => 1,
+ 157 => 1,
+ 158 => 1,
+ 159 => 1,
+ 160 => 1,
+ 161 => 1,
+ 162 => 1,
+ 163 => 1,
+ 164 => 1,
+ 165 => 1,
+ 166 => 1,
+ 167 => 1,
+ 169 => 1,
+ 170 => 1,
+ 171 => 1,
+ 173 => 1,
+ 174 => 1,
+ 175 => 1,
+ 207 => 1,
+ 227 => 1,
+ 229 => 1,
+ 230 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class MyClass
+{
+ $varName = 'hello';
+ $var_name = 'hello';
+ $varname = 'hello';
+ $_varName = 'hello';
+
+ public $varName = 'hello';
+ public $var_name = 'hello';
+ public $varname = 'hello';
+ public $_varName = 'hello';
+
+ protected $varName = 'hello';
+ protected $var_name = 'hello';
+ protected $varname = 'hello';
+ protected $_varName = 'hello';
+
+ private $_varName = 'hello';
+ private $_var_name = 'hello';
+ private $_varname = 'hello';
+ private $varName = 'hello';
+}
+
+class MyClass
+{
+ function func1()
+ {
+ function func2()
+ {
+ return $a;
+ }
+ return $data;
+ }
+}
+
+class MyClass
+{
+ public function prepare() {}
+ public function paint() {}
+}
+
+if (true) {
+ class MyClass
+ {
+ $varName = 'hello';
+ $var_name = 'hello';
+ }
+}
+
+class MyClass {
+ function myFunction($cc, $cv) {
+ $req = "delete from blah
+ where not (POP_{$cc}_A =
+'{$this->def["POP_{$cc}_A"]}'
+ and POP_{$cc}_B =
+'{$this->def["POP_{$cc}_B"]}')";
+ }
+}
+
+class mpgResponse{
+ var $term_id;
+ var $currentTag;
+ function characterHandler($parser,$data){
+ switch($this->currentTag)
+ {
+ case "term_id": {
+ $this->term_id=$data;
+ break;
+ }
+ }
+ }//end characterHandler
+}//end class mpgResponse
+
+class foo
+{
+ const bar = <<<BAZ
+qux
+BAZ;
+}
+
+class foo
+{
+ var $c = <<<C
+ccc
+C;
+}
+
+class a
+{
+ protected
+ $_sheet,
+ $_FieldParser,
+ $_key;
+}
+
+$util->setLogger(
+ new class {
+ private $varName = 'hello';
+ private $_varName = 'hello';
+});
--- /dev/null
+<?php
+/**
+ * Unit test class for the ValidVariableName 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\Standards\PEAR\Tests\NamingConventions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ValidVariableNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 12 => 1,
+ 17 => 1,
+ 22 => 1,
+ 92 => 1,
+ 93 => 1,
+ 94 => 1,
+ 99 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$someObject->someFunction("some", "parameter")
+->someOtherFunc(23, 42)->
+ someOtherFunc2($one, $two)
+
+ ->someOtherFunc3(23, 42)
+ ->andAThirdFunction();
+
+ $someObject->someFunction("some", "parameter")
+ ->someOtherFunc(23, 42);
+
+$someObject->someFunction("some", "parameter")->someOtherFunc(23, 42);
+
+$someObject->someFunction("some", "parameter")
+ ->someOtherFunc(23, 42);
+
+func(
+ $bar->foo()
+)
+ ->bar();
+
+func(
+ $bar->foo()
+)
+ ->bar(
+ $bar->foo()
+ ->bar()
+ ->func()
+ );
+
+$object
+ ->setBar($foo)
+ ->setFoo($bar);
+
+if ($bar) {
+ $object
+ ->setBar($foo)
+ ->setFoo($bar);
+}
+
+$response -> CompletedTrackDetails -> TrackDetails -> Events;
+$response
+ -> CompletedTrackDetails
+ -> TrackDetails
+ -> Events;
+
+$response
+ -> CompletedTrackDetails
+-> TrackDetails
+ -> Events;
--- /dev/null
+<?php
+/**
+ * Unit test class for the ObjectOperatorIndent 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\Standards\PEAR\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ObjectOperatorIndentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 2,
+ 6 => 1,
+ 15 => 1,
+ 27 => 1,
+ 37 => 1,
+ 38 => 1,
+ 48 => 1,
+ 49 => 1,
+ 50 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+class Test
+{
+ public function __construct()
+ {
+ }
+
+ function test1()
+ {
+ }
+
+ function test2() {}
+
+ private function _test3()
+ {
+ }
+
+}
+
+
+class Test2
+{
+ }
+
+
+public function test2()
+{
+ if ($str{0}) {
+ $chr = $str{0}; }
+
+
+ if (!class_exists($class_name)) {
+ echo $error;
+ }
+ $this->{$property} =& new $class_name($this->db_index);
+ $this->modules[$module] =& $this->{$property};
+}
+
+foreach ($elements as $element) {
+ if ($something) {
+ // Do IF.
+ } else if ($somethingElse) {
+ // Do ELSE.
+ }
+}
+
+switch ($foo) {
+case 1:
+ switch ($bar) {
+ default:
+ if ($something) {
+ echo $string{1};
+ } else if ($else) {
+ switch ($else) {
+ case 1:
+ // Do something.
+ break;
+ default:
+ // Do something.
+ break;
+ }
+ }
+ }
+break;
+case 2:
+ // Do something;
+ break;
+}
+
+switch ($httpResponseCode) {
+ case 100:
+ case 101:
+ case 102:
+ default:
+ return 'Unknown';
+}
+
+switch ($httpResponseCode) {
+ case 100:
+ case 101:
+ case 102:
+ return 'Processing.';
+ default:
+ return 'Unknown';
+}
+
+switch($i) {
+case 1: {}
+}
+
+switch ($httpResponseCode) {
+ case 100:
+ case 101:
+ case 102:
+ exit;
+ default:
+ exit;
+}
+
+if ($foo):
+ if ($bar):
+ $foo = 1;
+ elseif ($baz):
+ $foo = 2;
+ endif;
+endif;
+
+if ($foo):
+elseif ($baz): $foo = 2;
+endif;
+
+?>
+<ul>
+ <?php foreach ($array as $value) : ?>
+ <li><?php echo $value ?></li>
+ <?php endforeach ?>
+</ul>
+<ul>
+ <?php foreach ($array as $value) : ?>
+ <li><?php echo $value ?></li>
+<?php endforeach ?>
+</ul>
+<ul>
+ <?php foreach ($array as $value) : ?>
+ <li><?php echo $value ?></li>
+ <?php endforeach ?>
+</ul>
+<?php
+switch ( $a ) {
+ case 'foo':
+ do {
+ $a = 'b';
+ } while ( $a );
+ return 5;
+
+ case 'bar':
+ foreach ( $a as $b ) {
+ $e = 'b';
+ }
+ return 5;
+}
+
+?>
+<?php $_cartQty = $this->getSummaryCount(); ?>
+<div id="minicart" <?php if ($_cartQty == 0): ?>class="empty"<?php endif; ?>>
--- /dev/null
+<?php
+
+class Test
+{
+ public function __construct()
+ {
+ }
+
+ function test1()
+ {
+ }
+
+ function test2() {
+ }
+
+ private function _test3()
+ {
+ }
+
+}
+
+
+class Test2
+{
+}
+
+
+public function test2()
+{
+ if ($str{0}) {
+ $chr = $str{0};
+ }
+
+
+ if (!class_exists($class_name)) {
+ echo $error;
+ }
+ $this->{$property} =& new $class_name($this->db_index);
+ $this->modules[$module] =& $this->{$property};
+}
+
+foreach ($elements as $element) {
+ if ($something) {
+ // Do IF.
+ } else if ($somethingElse) {
+ // Do ELSE.
+ }
+}
+
+switch ($foo) {
+case 1:
+ switch ($bar) {
+ default:
+ if ($something) {
+ echo $string{1};
+ } else if ($else) {
+ switch ($else) {
+ case 1:
+ // Do something.
+ break;
+ default:
+ // Do something.
+ break;
+ }
+ }
+ }
+ break;
+case 2:
+ // Do something;
+ break;
+}
+
+switch ($httpResponseCode) {
+ case 100:
+ case 101:
+ case 102:
+ default:
+ return 'Unknown';
+}
+
+switch ($httpResponseCode) {
+ case 100:
+ case 101:
+ case 102:
+ return 'Processing.';
+ default:
+ return 'Unknown';
+}
+
+switch($i) {
+case 1: {
+ }
+}
+
+switch ($httpResponseCode) {
+ case 100:
+ case 101:
+ case 102:
+ exit;
+ default:
+ exit;
+}
+
+if ($foo):
+ if ($bar):
+ $foo = 1;
+ elseif ($baz):
+ $foo = 2;
+ endif;
+endif;
+
+if ($foo):
+elseif ($baz): $foo = 2;
+endif;
+
+?>
+<ul>
+ <?php foreach ($array as $value) : ?>
+ <li><?php echo $value ?></li>
+ <?php endforeach ?>
+</ul>
+<ul>
+ <?php foreach ($array as $value) : ?>
+ <li><?php echo $value ?></li>
+ <?php endforeach ?>
+</ul>
+<ul>
+ <?php foreach ($array as $value) : ?>
+ <li><?php echo $value ?></li>
+ <?php endforeach ?>
+</ul>
+<?php
+switch ( $a ) {
+ case 'foo':
+ do {
+ $a = 'b';
+ } while ( $a );
+ return 5;
+
+ case 'bar':
+ foreach ( $a as $b ) {
+ $e = 'b';
+ }
+ return 5;
+}
+
+?>
+<?php $_cartQty = $this->getSummaryCount(); ?>
+<div id="minicart" <?php if ($_cartQty == 0): ?>class="empty"<?php
+endif; ?>>
--- /dev/null
+<?php
+/**
+ * Unit test class for the ScopeClosingBrace 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\Standards\PEAR\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ScopeClosingBraceUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 11 => 1,
+ 13 => 1,
+ 24 => 1,
+ 30 => 1,
+ 61 => 1,
+ 65 => 1,
+ 85 => 1,
+ 89 => 1,
+ 98 => 1,
+ 122 => 1,
+ 127 => 1,
+ 135 => 1,
+ 141 => 1,
+ 146 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+class Test
+{
+ function __construct()
+ {
+ $this->hello(); // error here
+ }
+
+ function hello() // error here
+ { // no error here as brackets can be put anywhere in the pear standard
+ echo 'hello';
+ }
+
+ function hello2()
+ {
+ if (TRUE) { // error here
+ echo 'hello'; // no error here as its more than 4 spaces.
+ } else {
+ echo 'bye'; // error here
+ }
+
+ while (TRUE) {
+ echo 'hello'; // error here
+ }
+
+ do { // error here
+ echo 'hello'; // error here
+ } while (TRUE);
+ }
+
+ function hello3()
+ {
+ switch ($hello) {
+ case 'hello':
+ break;
+ }
+ }
+
+}
+
+?>
+<pre>
+</head>
+<body>
+<?php
+if ($form->validate()) {
+ $safe = $form->getSubmitValues();
+}
+?>
+</pre>
+<?php
+
+class Test2
+{
+ function __construct()
+ {
+ // $this->open(); // error here
+ }
+
+ public function open()
+ {
+ // Some inline stuff that shouldn't error
+ if (TRUE) echo 'hello';
+ foreach ($tokens as $token) echo $token;
+ }
+
+ /**
+ * This is a comment 1.
+ * This is a comment 2.
+ * This is a comment 3.
+ * This is a comment 4.
+ */
+ public function close()
+ {
+ // All ok.
+ if (TRUE) {
+ if (TRUE) {
+ } else if (FALSE) {
+ foreach ($tokens as $token) {
+ switch ($token) {
+ case '1':
+ case '2':
+ if (true) {
+ if (false) {
+ if (false) {
+ if (false) {
+ echo 'hello';
+ }
+ }
+ }
+ }
+ break;
+ case '5':
+ break;
+ }
+ do {
+ while (true) {
+ foreach ($tokens as $token) {
+ for ($i = 0; $i < $token; $i++) {
+ echo 'hello';
+ }
+ }
+ }
+ } while (true);
+ }
+ }
+ }
+ }
+
+ /*
+ This is another c style comment 1.
+ This is another c style comment 2.
+ This is another c style comment 3.
+ This is another c style comment 4.
+ This is another c style comment 5.
+ */
+
+ /*
+ *
+ *
+ *
+ */
+
+ /**
+ */
+
+ /*
+ This comment has a newline in it.
+
+ */
+
+ public function read()
+ {
+ echo 'hello';
+
+ // no errors below.
+ $array = array(
+ 'this',
+ 'that' => array(
+ 'hello',
+ 'hello again' => array(
+ 'hello',
+ ),
+ ),
+ );
+ }
+}
+
+abstract class Test3
+{
+ public function parse()
+ {
+
+ foreach ($t as $ndx => $token) {
+ if (is_array($token)) {
+ echo 'here';
+ } else {
+ $ts[] = array("token" => $token, "value" => '');
+
+ $last = count($ts) - 1;
+
+ switch ($token) {
+ case '(':
+
+ if ($last >= 3 &&
+ $ts[0]['token'] != T_CLASS &&
+ $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
+ $ts[$last - 3]['token'] == T_VARIABLE ) {
+
+
+ if (true) {
+ echo 'hello';
+ }
+ }
+ array_push($braces, $token);
+ break;
+ }
+ }
+ }
+ }
+}
+
+public function test()
+{
+ $o = <<<EOF
+this is some text
+this is some text
+this is some text
+this is some text
+this is some text
+this is some text
+EOF;
+
+ return $o;
+}
+
+if ($a === true || $a === true || $a === true || $a === true ||
+ $a === true || $a === true || $a === true || $a === true) {
+
+ echo 'hello';
+}
+
+if ($true) {
+ /* First comment line
+ *
+ * Comment test here
+ * Comment test here
+ *
+ */
+
+ /* First comment line
+ *
+ * Comment test here
+ * Comment test here
+ *
+ this si something */
+}
+
+function test()
+{
+ /* taken from http://de3.php.net/manual/en/reserved.php */
+ # $m[] = 'declare';
+ /* taken from http://de3.php.net/manual/en/reserved.php */
+ # $m[] = 'declare';
+}
+
+foreach ($elements as $element) {
+ if ($something) {
+ // Do IF.
+ } else if ($somethingElse) {
+ // Do ELSE.
+ }
+}
+
+if ($condition) {
+ echo "This is a long
+string that spans $numLines lines
+without indenting.
+";
+}
+
+if ($condition) {
+ echo 'This is a long
+ string that spans multiple lines
+ with indenting.
+ ';
+}
+
+if ($condition) {
+ echo 'This is a long
+ string that spans multiple lines
+ with indenting.';
+}
+
+switch ($foo) {
+case 1:
+ switch ($bar) {
+ default:
+ echo $string{1};
+ }
+ break;
+}
+
+function temp($foo, $bar) {
+ switch ($foo) {
+ case 1:
+ switch ($bar) {
+ default:
+ return $foo;
+ }
+ break;
+ }
+}
+
+switch ($foo) {
+case 1:
+ switch ($bar) {
+ default:
+ if ($something) {
+ echo $string{1};
+ } else if ($else) {
+ switch ($else) {
+ default:
+ }
+ }
+ }
+ break;
+}
+
+switch ($name) {
+case "1":
+case "2":
+case "3":
+ return true;
+}
+
+switch ($name) {
+case "1":
+case "2":
+case "3":
+default :
+ return true;
+}
+
+// Don't check the first token in the closure.
+$array = array();
+array_map(
+ function($x)
+ {
+ return trim($x);
+ },
+ $array
+);
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ScopeIndent 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\Standards\PEAR\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ScopeIndentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 7 => 1,
+ 10 => 1,
+ 17 => 1,
+ 20 => 1,
+ 24 => 1,
+ 25 => 1,
+ 27 => 1,
+ 28 => 1,
+ 29 => 1,
+ 30 => 1,
+ 58 => 1,
+ 123 => 1,
+ 224 => 1,
+ 225 => 1,
+ 279 => 1,
+ 284 => 1,
+ 311 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?xml version="1.0"?>
+<ruleset name="PEAR">
+ <description>The PEAR coding standard.</description>
+
+ <!-- Include some additional sniffs from the Generic standard -->
+ <rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
+ <rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
+ <rule ref="Generic.PHP.LowerCaseConstant"/>
+ <rule ref="Generic.PHP.DisallowShortOpenTag"/>
+ <rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
+ <rule ref="Generic.Commenting.DocComment"/>
+ <rule ref="Squiz.Commenting.DocCommentAlignment"/>
+
+ <!-- Lines can be 85 chars long, but never show errors -->
+ <rule ref="Generic.Files.LineLength">
+ <properties>
+ <property name="lineLimit" value="85"/>
+ <property name="absoluteLineLimit" value="0"/>
+ </properties>
+ </rule>
+
+ <!-- Use Unix newlines -->
+ <rule ref="Generic.Files.LineEndings">
+ <properties>
+ <property name="eolChar" value="\n"/>
+ </properties>
+ </rule>
+
+ <!-- This message is not required as spaces are allowed for alignment -->
+ <rule ref="Generic.Functions.FunctionCallArgumentSpacing.TooMuchSpaceAfterComma">
+ <severity>0</severity>
+ </rule>
+
+ <!-- Use warnings for inline control structures -->
+ <rule ref="Generic.ControlStructures.InlineControlStructure">
+ <properties>
+ <property name="error" value="false"/>
+ </properties>
+ </rule>
+
+</ruleset>
--- /dev/null
+<documentation title="Class Declaration">
+ <standard>
+ <![CDATA[
+ Each class must be in a file by itself and must be under a namespace (a top-level vendor name).
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: One class in a file.">
+ <![CDATA[
+<?php
+namespace Foo;
+
+<em>class Bar</em> {
+}
+ ]]>
+ </code>
+ <code title="Invalid: Multiple classes in a single file.">
+ <![CDATA[
+<?php
+namespace Foo;
+
+<em>class Bar</em> {
+}
+
+<em>class Baz</em> {
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: A vendor-level namespace is used.">
+ <![CDATA[
+<?php
+<em>namespace Foo;</em>
+
+class Bar {
+}
+ ]]>
+ </code>
+ <code title="Invalid: No namespace used in file.">
+ <![CDATA[
+<?php
+class Bar {
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Side Effects">
+ <standard>
+ <![CDATA[
+ A php file should either contain declarations with no side effects, or should just have logic (including side effects) with no declarations.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A class defined in a file by itself.">
+ <![CDATA[
+<?php
+class Foo
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: A class defined in a file with other code.">
+ <![CDATA[
+<?php
+class Foo
+{
+}
+
+<em>echo "Class Foo loaded."</em>
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<?php
+/**
+ * Checks the declaration of the class is correct.
+ *
+ * @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\Standards\PSR1\Sniffs\Classes;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ClassDeclarationSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_CLASS,
+ T_INTERFACE,
+ T_TRAIT,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param integer $stackPtr The position of the current token in
+ * the token stack.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ if (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ return;
+ }
+
+ $errorData = array(strtolower($tokens[$stackPtr]['content']));
+
+ $nextClass = $phpcsFile->findNext(array(T_CLASS, T_INTERFACE, T_TRAIT), ($tokens[$stackPtr]['scope_closer'] + 1));
+ if ($nextClass !== false) {
+ $error = 'Each %s must be in a file by itself';
+ $phpcsFile->addError($error, $nextClass, 'MultipleClasses', $errorData);
+ $phpcsFile->recordMetric($stackPtr, 'One class per file', 'no');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'One class per file', 'yes');
+ }
+
+ $namespace = $phpcsFile->findNext(array(T_NAMESPACE, T_CLASS, T_INTERFACE, T_TRAIT), 0);
+ if ($tokens[$namespace]['code'] !== T_NAMESPACE) {
+ $error = 'Each %s must be in a namespace of at least one level (a top-level vendor name)';
+ $phpcsFile->addError($error, $stackPtr, 'MissingNamespace', $errorData);
+ $phpcsFile->recordMetric($stackPtr, 'Class defined in namespace', 'no');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Class defined in namespace', 'yes');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures a file declares new symbols and causes no other side effects, or executes logic with side effects, but not both.
+ *
+ * @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\Standards\PSR1\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class SideEffectsSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the token stack.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $result = $this->searchForConflict($phpcsFile, 0, ($phpcsFile->numTokens - 1), $tokens);
+
+ if ($result['symbol'] !== null && $result['effect'] !== null) {
+ $error = 'A file should declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it should execute logic with side effects, but should not do both. The first symbol is defined on line %s and the first side effect is on line %s.';
+ $data = array(
+ $tokens[$result['symbol']]['line'],
+ $tokens[$result['effect']]['line'],
+ );
+ $phpcsFile->addWarning($error, 0, 'FoundWithSymbols', $data);
+ $phpcsFile->recordMetric($stackPtr, 'Declarations and side effects mixed', 'yes');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Declarations and side effects mixed', 'no');
+ }
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+ /**
+ * Searches for symbol declarations and side effects.
+ *
+ * Returns the positions of both the first symbol declared and the first
+ * side effect in the file. A NULL value for either indicates nothing was
+ * found.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $start The token to start searching from.
+ * @param int $end The token to search to.
+ * @param array $tokens The stack of tokens that make up
+ * the file.
+ *
+ * @return array
+ */
+ private function searchForConflict($phpcsFile, $start, $end, $tokens)
+ {
+ $symbols = array(
+ T_CLASS => T_CLASS,
+ T_INTERFACE => T_INTERFACE,
+ T_TRAIT => T_TRAIT,
+ T_FUNCTION => T_FUNCTION,
+ );
+
+ $conditions = array(
+ T_IF => T_IF,
+ T_ELSE => T_ELSE,
+ T_ELSEIF => T_ELSEIF,
+ );
+
+ $firstSymbol = null;
+ $firstEffect = null;
+ for ($i = $start; $i <= $end; $i++) {
+ // Ignore whitespace and comments.
+ if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
+ continue;
+ }
+
+ // Ignore PHP tags.
+ if ($tokens[$i]['code'] === T_OPEN_TAG
+ || $tokens[$i]['code'] === T_CLOSE_TAG
+ ) {
+ continue;
+ }
+
+ // Ignore shebang.
+ if (substr($tokens[$i]['content'], 0, 2) === '#!') {
+ continue;
+ }
+
+ // Ignore entire namespace, declare, const and use statements.
+ if ($tokens[$i]['code'] === T_NAMESPACE
+ || $tokens[$i]['code'] === T_USE
+ || $tokens[$i]['code'] === T_DECLARE
+ || $tokens[$i]['code'] === T_CONST
+ ) {
+ if (isset($tokens[$i]['scope_opener']) === true) {
+ $i = $tokens[$i]['scope_closer'];
+ } else {
+ $semicolon = $phpcsFile->findNext(T_SEMICOLON, ($i + 1));
+ if ($semicolon !== false) {
+ $i = $semicolon;
+ }
+ }
+
+ continue;
+ }
+
+ // Ignore function/class prefixes.
+ if (isset(Tokens::$methodPrefixes[$tokens[$i]['code']]) === true) {
+ continue;
+ }
+
+ // Ignore anon classes.
+ if ($tokens[$i]['code'] === T_ANON_CLASS) {
+ $i = $tokens[$i]['scope_closer'];
+ continue;
+ }
+
+ // Detect and skip over symbols.
+ if (isset($symbols[$tokens[$i]['code']]) === true
+ && isset($tokens[$i]['scope_closer']) === true
+ ) {
+ if ($firstSymbol === null) {
+ $firstSymbol = $i;
+ }
+
+ $i = $tokens[$i]['scope_closer'];
+ continue;
+ } else if ($tokens[$i]['code'] === T_STRING
+ && strtolower($tokens[$i]['content']) === 'define'
+ ) {
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($i - 1), null, true);
+ if ($tokens[$prev]['code'] !== T_OBJECT_OPERATOR) {
+ if ($firstSymbol === null) {
+ $firstSymbol = $i;
+ }
+
+ $semicolon = $phpcsFile->findNext(T_SEMICOLON, ($i + 1));
+ if ($semicolon !== false) {
+ $i = $semicolon;
+ }
+
+ continue;
+ }
+ }//end if
+
+ // Conditional statements are allowed in symbol files as long as the
+ // contents is only a symbol definition. So don't count these as effects
+ // in this case.
+ if (isset($conditions[$tokens[$i]['code']]) === true) {
+ if (isset($tokens[$i]['scope_opener']) === false) {
+ // Probably an "else if", so just ignore.
+ continue;
+ }
+
+ $result = $this->searchForConflict(
+ $phpcsFile,
+ ($tokens[$i]['scope_opener'] + 1),
+ ($tokens[$i]['scope_closer'] - 1),
+ $tokens
+ );
+
+ if ($result['symbol'] !== null) {
+ if ($firstSymbol === null) {
+ $firstSymbol = $result['symbol'];
+ }
+
+ if ($result['effect'] !== null) {
+ // Found a conflict.
+ $firstEffect = $result['effect'];
+ break;
+ }
+ }
+
+ if ($firstEffect === null) {
+ $firstEffect = $result['effect'];
+ }
+
+ $i = $tokens[$i]['scope_closer'];
+ continue;
+ }//end if
+
+ if ($firstEffect === null) {
+ $firstEffect = $i;
+ }
+
+ if ($firstSymbol !== null) {
+ // We have a conflict we have to report, so no point continuing.
+ break;
+ }
+ }//end for
+
+ return array(
+ 'symbol' => $firstSymbol,
+ 'effect' => $firstEffect,
+ );
+
+ }//end searchForConflict()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures method names are defined using camel case.
+ *
+ * @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\Standards\PSR1\Sniffs\Methods;
+
+use PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions\CamelCapsFunctionNameSniff as GenericCamelCapsFunctionNameSniff;
+use PHP_CodeSniffer\Util\Common;
+use PHP_CodeSniffer\Files\File;
+
+class CamelCapsMethodNameSniff extends GenericCamelCapsFunctionNameSniff
+{
+
+
+ /**
+ * Processes the tokens within the scope.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed.
+ * @param int $stackPtr The position where this token was
+ * found.
+ * @param int $currScope The position of the current scope.
+ *
+ * @return void
+ */
+ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
+ {
+ $methodName = $phpcsFile->getDeclarationName($stackPtr);
+ if ($methodName === null) {
+ // Ignore closures.
+ return;
+ }
+
+ // Ignore magic methods.
+ if (preg_match('|^__[^_]|', $methodName) !== 0) {
+ $magicPart = strtolower(substr($methodName, 2));
+ if (isset($this->magicMethods[$magicPart]) === true
+ || isset($this->methodsDoubleUnderscore[$magicPart]) === true
+ ) {
+ return;
+ }
+ }
+
+ $testName = ltrim($methodName, '_');
+ if ($testName !== '' && Common::isCamelCaps($testName, false, true, false) === false) {
+ $error = 'Method name "%s" is not in camel caps format';
+ $className = $phpcsFile->getDeclarationName($currScope);
+ $errorData = array($className.'::'.$methodName);
+ $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData);
+ $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'no');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'yes');
+ }
+
+ }//end processTokenWithinScope()
+
+
+ /**
+ * Processes the tokens outside the scope.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed.
+ * @param int $stackPtr The position where this token was
+ * found.
+ *
+ * @return void
+ */
+ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
+ {
+
+ }//end processTokenOutsideScope()
+
+
+}//end class
--- /dev/null
+<?php
+class Foo {}
+class Bar {}
--- /dev/null
+<?php
+namespace MyProject;
+
+class Foo {}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClassDeclaration 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\Standards\PSR1\Tests\Classes;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClassDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ if ($testFile === 'ClassDeclarationUnitTest.2.inc') {
+ return array();
+ }
+
+ if (PHP_VERSION_ID >= 50300) {
+ return array(
+ 2 => 1,
+ 3 => 2,
+ );
+ } else {
+ return array(3 => 1);
+ }
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+namespace Vendor\Model;
+
+const CONSTANT1 = 1;
+const CONSTANT2 = 2;
+
+use Something;
+use SomethingElse;
+
+declare(ticks=1);
+
+declare(ticks=1) {
+ // Code.
+}
+
+define("MAXSIZE", 100);
+if (defined('MINSIZE') === false) {
+ define("MINSIZE", 10);
+}
+
+// Class comment.
+final class Foo
+{
+ public $myvar = 'foo';
+
+ // Function comment.
+ function Bar() {
+ echo 'hi';
+ }
+}
+
+function MyFunction() {
+ echo 'hi';
+}
+
+if (!function_exists('YourFunction')) {
+ // Define a function.
+ function YourFunction() {
+ echo 'hi';
+ }
+}
+
+if (!class_exists('MyClass')) {
+ // Define a class.
+ class MyClass
+ {
+ function SomeFunction() {}
+ }
+} else if (!interface_exists('MyInterface')) {
+ interface MyInterface {}
+} elseif (!interface_exists('MyInterface')) {
+ interface MyInterface {}
+} else {
+ interface MyInterface {}
+}
+
+if (!interface_exists('MyInterface')) {
+ // Define an interface.
+ interface MyInterface {}
+}
+
+namespace {
+ class A {}
+}
+
+?>
--- /dev/null
+<?php
+include 'Somefile';
+
+const CONSTANT1 = 1;
+const CONSTANT2 = 2;
+
+if ($something) {
+ echo 'hi';
+}
+
+$var = myFunction();
+print_r($var);
+echo $object->define();
+echo $object -> define();
+
+$c = new class extends Something{
+
+ public function someMethod()
+ {
+ // ...
+ }
+
+};
--- /dev/null
+<?php
+define("MAXSIZE", 100);
+$defined = true;
+if (defined('MINSIZE') === false) {
+ $defined = false;
+}
--- /dev/null
+<html>
+<?php
+function printHead() {
+ echo '<head></head>';
+}
+
+printHead();
+?>
+<body></body>
+</html>
--- /dev/null
+<?php
+define('SOME_VERSION', '0.1-foo') ?>
--- /dev/null
+<?php
+namespace Bar;
+
+use some\foo\{ClassA, ClassB, ClassC as C};
+use function some\foo\{fn_a, fn_b, fn_c};
+use const some\foo\{ConstA, ConstB, ConstC};
+
+class Bar {
+}
--- /dev/null
+#!/usr/bin/env php
+<?php
+
+//some code
+function foo()
+{
+ return 'bar';
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the SideEffects 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\Standards\PSR1\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class SideEffectsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='')
+ {
+ switch ($testFile) {
+ case 'SideEffectsUnitTest.3.inc':
+ case 'SideEffectsUnitTest.4.inc':
+ case 'SideEffectsUnitTest.5.inc':
+ return array(1 => 1);
+ default:
+ return array();
+ }//end switch
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+abstract class My_Class {
+
+ public function __construct() {}
+ public function My_Class() {}
+ public function _My_Class() {}
+
+ public function getSomeValue() {}
+ public function parseMyDSN() {}
+ public function get_some_value() {}
+ public function GetSomeValue() {}
+ public function getSomeValue_Again() {}
+
+ protected function getSomeValue() {}
+ protected function parseMyDSN() {}
+ protected function get_some_value() {}
+
+ private function getSomeValue() {}
+ private function parseMyDSN() {}
+ private function get_some_value() {}
+
+ function getSomeValue() {}
+ function parseMyDSN() {}
+ function get_some_value() {}
+ function o_toString() {}
+
+}//end class
+
+function getSomeValue() {}
+function parseMyDSN() {}
+function get_some_value() {}
+
+class Closure_Test {
+ function test() {
+ $foo = function() { echo 'foo'; };
+ }
+}
+
+trait Foo
+{
+ function __call() {}
+}
+
+class Magic_Case_Test {
+ function __Construct() {}
+ function __isSet() {}
+ function __tostring() {}
+ function __set_state() {}
+}
+function __autoLoad() {}
+
+class Foo extends \SoapClient
+{
+ public function __soapCall(
+ $functionName,
+ $arguments,
+ $options = array(),
+ $inputHeaders = null,
+ &$outputHeaders = array()
+ ) {
+ // body
+ }
+
+ function __() {}
+}
+
+function ___tripleUnderscore() {} // Ok.
+
+class triple {
+ public function ___tripleUnderscore() {} // Ok.
+}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the CamelCapsMethodName 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\Standards\PSR1\Tests\Methods;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class CamelCapsMethodNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 6 => 1,
+ 7 => 1,
+ 11 => 1,
+ 12 => 1,
+ 13 => 1,
+ 17 => 1,
+ 21 => 1,
+ 25 => 1,
+ 26 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?xml version="1.0"?>
+<ruleset name="PSR1">
+ <description>The PSR1 coding standard.</description>
+
+ <!-- 2. Files -->
+
+ <!-- 2.1. PHP Tags -->
+
+ <!-- PHP code MUST use the long <?php ?> tags or the short-echo <?= ?> tags; it MUST NOT use the other tag variations. -->
+ <rule ref="Generic.PHP.DisallowShortOpenTag.EchoFound">
+ <severity>0</severity>
+ </rule>
+
+ <!-- 2.2. Character Encoding -->
+
+ <!-- PHP code MUST use only UTF-8 without BOM. -->
+ <rule ref="Generic.Files.ByteOrderMark"/>
+
+ <!-- 2.3. Side Effects -->
+
+ <!-- A file SHOULD declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it SHOULD execute logic with side effects, but SHOULD NOT do both. -->
+ <!-- checked in Files/SideEffectsSniff -->
+
+ <!-- 3. Namespace and Class Names -->
+
+ <!-- Namespaces and classes MUST follow PSR-0.
+ This means each class is in a file by itself, and is in a namespace of at least one level: a top-level vendor name. -->
+ <!-- checked in Classes/ClassDeclarationSniff -->
+
+ <!-- Class names MUST be declared in StudlyCaps. -->
+ <rule ref="Squiz.Classes.ValidClassName"/>
+
+ <!-- 4. Class Constants, Properties, and Methods -->
+
+ <!-- 4.1. Constants -->
+
+ <!-- Class constants MUST be declared in all upper case with underscore separators. -->
+ <rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
+
+ <!-- 4.3. Methods -->
+
+ <!-- Method names MUST be declared in camelCase(). -->
+ <!-- checked in Methods/CamelCapsMethodNameSniff -->
+
+</ruleset>
--- /dev/null
+<documentation title="Class Declarations">
+ <standard>
+ <![CDATA[
+ There should be exactly 1 space between the abstract or final keyword and the class keyword and between the class keyword and the class name. The extends and implements keywords, if present, must be on the same line as the class name. When interfaces implemented are spread over multiple lines, there should be exactly 1 interface mentioned per line indented by 1 level. The closing brace of the class must go on the first line after the body of the class and must be on a line by itself.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Correct spacing around class keyword.">
+ <![CDATA[
+abstract<em> </em>class<em> </em>Foo
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: 2 spaces used around class keyword.">
+ <![CDATA[
+abstract<em> </em>class<em> </em>Foo
+{
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Property Declarations">
+ <standard>
+ <![CDATA[
+ Property names should not be prefixed with an underscore to indicate visibility. Visibility should be used to declare properties rather than the var keyword. Only one property should be declared within a statement.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Correct property naming.">
+ <![CDATA[
+class Foo
+{
+ private $<em>bar</em>;
+}
+ ]]>
+ </code>
+ <code title="Invalid: An underscore prefix used to indicate visibility.">
+ <![CDATA[
+class Foo
+{
+ private $<em>_bar</em>;
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Visibility of property declared.">
+ <![CDATA[
+class Foo
+{
+ <em>private</em> $bar;
+}
+ ]]>
+ </code>
+ <code title="Invalid: Var keyword used to declare property.">
+ <![CDATA[
+class Foo
+{
+ <em>var</em> $bar;
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: One property declared per statement.">
+ <![CDATA[
+class Foo
+{
+ private $bar;
+ private $baz;
+}
+ ]]>
+ </code>
+ <code title="Invalid: Multiple properties declared in one statement.">
+ <![CDATA[
+class Foo
+{
+ private <em>$bar, $baz</em>;
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Control Structure Spacing">
+ <standard>
+ <![CDATA[
+ Control Structures should have 0 spaces after opening parentheses and 0 spaces before closing parentheses.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Correct spacing.">
+ <![CDATA[
+if (<em></em>$foo<em></em>) {
+ $var = 1;
+}
+ ]]>
+ </code>
+ <code title="Invalid: Whitespace used inside the parentheses.">
+ <![CDATA[
+if (<em> </em>$foo<em> </em>) {
+ $var = 1;
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Elseif Declarations">
+ <standard>
+ <![CDATA[
+ PHP's elseif keyword should be used instead of else if.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Single word elseif keyword used.">
+ <![CDATA[
+if ($foo) {
+ $var = 1;
+} <em>elseif</em> ($bar) {
+ $var = 2;
+}
+ ]]>
+ </code>
+ <code title="Invalid: Separate else and if keywords used.">
+ <![CDATA[
+if ($foo) {
+ $var = 1;
+} <em>else if</em> ($bar) {
+ $var = 2;
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Switch Declarations">
+ <standard>
+ <![CDATA[
+ Case statements should be indented 4 spaces from the switch keyword. It should also be followed by a space. Colons in switch declarations should not be preceded by whitespace. Break statements should be indented 4 more spaces from the case statement. There must be a comment when falling through from one case into the next.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Case statement indented correctly.">
+ <![CDATA[
+switch ($foo) {
+<em> </em>case 'bar':
+ break;
+}
+ ]]>
+ </code>
+ <code title="Invalid: Case statement not indented 4 spaces.">
+ <![CDATA[
+switch ($foo) {
+<em></em>case 'bar':
+ break;
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Case statement followed by 1 space.">
+ <![CDATA[
+switch ($foo) {
+ case<em> </em>'bar':
+ break;
+}
+ ]]>
+ </code>
+ <code title="Invalid: Case statement not followed by 1 space.">
+ <![CDATA[
+switch ($foo) {
+ case<em></em>'bar':
+ break;
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Colons not prefixed by whitespace.">
+ <![CDATA[
+switch ($foo) {
+ case 'bar'<em></em>:
+ break;
+ default<em></em>:
+ break;
+}
+ ]]>
+ </code>
+ <code title="Invalid: Colons prefixed by whitespace.">
+ <![CDATA[
+switch ($foo) {
+ case 'bar'<em> </em>:
+ break;
+ default<em> </em>:
+ break;
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Break statement indented correctly.">
+ <![CDATA[
+switch ($foo) {
+ case 'bar':
+<em> </em>break;
+}
+ ]]>
+ </code>
+ <code title="Invalid: Break statement not indented 4 spaces.">
+ <![CDATA[
+switch ($foo) {
+ case 'bar':
+<em> </em>break;
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Comment marking intentional fall-through.">
+ <![CDATA[
+switch ($foo) {
+ case 'bar':
+ <em>// no break</em>
+ default<em></em>:
+ break;
+}
+ ]]>
+ </code>
+ <code title="Invalid: No comment marking intentional fall-through.">
+ <![CDATA[
+switch ($foo) {
+ case 'bar':
+ default<em></em>:
+ break;
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="End File Newline">
+ <standard>
+ <![CDATA[
+ PHP Files should end with exactly one newline.
+ ]]>
+ </standard>
+</documentation>
--- /dev/null
+<documentation title="Method Declarations">
+ <standard>
+ <![CDATA[
+ Method names should not be prefixed with an underscore to indicate visibility. The static keyword, when present, should come after the visibility declaration, and the final and abstract keywords should come before.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Correct method naming.">
+ <![CDATA[
+class Foo
+{
+ private function <em>bar</em>()
+ {
+ }
+}
+ ]]>
+ </code>
+ <code title="Invalid: An underscore prefix used to indicate visibility.">
+ <![CDATA[
+class Foo
+{
+ private function <em>_bar</em>()
+ {
+ }
+}
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Correct ordering of method prefixes.">
+ <![CDATA[
+class Foo
+{
+ <em>final public static</em> function <em>bar</em>()
+ {
+ }
+}
+ ]]>
+ </code>
+ <code title="Invalid: static keyword used before visibility and final used after.">
+ <![CDATA[
+class Foo
+{
+ <em>static public final</em> function <em>bar</em>()
+ {
+ }
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Namespace Declarations">
+ <standard>
+ <![CDATA[
+ There must be one blank line after the namespace declaration.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: One blank line after the namespace declaration.">
+ <![CDATA[
+namespace \Foo\Bar;
+<em></em>
+use \Baz;
+ ]]>
+ </code>
+ <code title="Invalid: No blank line after the namespace declaration.">
+ <![CDATA[
+namespace \Foo\Bar;
+use \Baz;
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Namespace Declarations">
+ <standard>
+ <![CDATA[
+ Each use declaration must contain only one namespace and must come after the first namespace declaration. There should be one blank line after the final use statement.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: One use declaration per namespace.">
+ <![CDATA[
+use \Foo;
+use \Bar;
+ ]]>
+ </code>
+ <code title="Invalid: Multiple namespaces in a use declaration.">
+ <![CDATA[
+use <em>\Foo, \Bar</em>;
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Use statements come after first namespace.">
+ <![CDATA[
+namespace \Foo;
+
+use \Bar;
+ ]]>
+ </code>
+ <code title="Invalid: Namespace declared after use.">
+ <![CDATA[
+use \Bar;
+
+namespace \Foo;
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: A single blank line after the final use statement.">
+ <![CDATA[
+use \Foo;
+use \Bar;
+<em></em>
+class Baz
+{
+}
+ ]]>
+ </code>
+ <code title="Invalid: No blank line after the final use statement.">
+ <![CDATA[
+use \Foo;
+use \Bar;
+class Baz
+{
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<?php
+/**
+ * Checks the declaration of the class and its inheritance is correct.
+ *
+ * @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\Standards\PSR2\Sniffs\Classes;
+
+use PHP_CodeSniffer\Standards\PEAR\Sniffs\Classes\ClassDeclarationSniff as PEARClassDeclarationSniff;
+use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Files\File;
+
+class ClassDeclarationSniff extends PEARClassDeclarationSniff
+{
+
+ /**
+ * The number of spaces code should be indented.
+ *
+ * @var integer
+ */
+ public $indent = 4;
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ // We want all the errors from the PEAR standard, plus some of our own.
+ parent::process($phpcsFile, $stackPtr);
+
+ // Just in case.
+ $tokens = $phpcsFile->getTokens();
+ if (isset($tokens[$stackPtr]['scope_opener']) === false) {
+ return;
+ }
+
+ $this->processOpen($phpcsFile, $stackPtr);
+ $this->processClose($phpcsFile, $stackPtr);
+
+ }//end process()
+
+
+ /**
+ * Processes the opening section of a class declaration.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function processOpen(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $stackPtrType = strtolower($tokens[$stackPtr]['content']);
+
+ // Check alignment of the keyword and braces.
+ if ($tokens[($stackPtr - 1)]['code'] === T_WHITESPACE) {
+ $prevContent = $tokens[($stackPtr - 1)]['content'];
+ if ($prevContent !== $phpcsFile->eolChar) {
+ $blankSpace = substr($prevContent, strpos($prevContent, $phpcsFile->eolChar));
+ $spaces = strlen($blankSpace);
+
+ if (in_array($tokens[($stackPtr - 2)]['code'], array(T_ABSTRACT, T_FINAL)) === true
+ && $spaces !== 1
+ ) {
+ $prevContent = strtolower($tokens[($stackPtr - 2)]['content']);
+ $error = 'Expected 1 space between %s and %s keywords; %s found';
+ $data = array(
+ $prevContent,
+ $stackPtrType,
+ $spaces,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceBeforeKeyword', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
+ }
+ }
+ } else if ($tokens[($stackPtr - 2)]['code'] === T_ABSTRACT
+ || $tokens[($stackPtr - 2)]['code'] === T_FINAL
+ ) {
+ $prevContent = strtolower($tokens[($stackPtr - 2)]['content']);
+ $error = 'Expected 1 space between %s and %s keywords; newline found';
+ $data = array(
+ $prevContent,
+ $stackPtrType,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NewlineBeforeKeyword', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
+ }
+ }//end if
+ }//end if
+
+ // We'll need the indent of the class/interface declaration for later.
+ $classIndent = 0;
+ for ($i = ($stackPtr - 1); $i > 0; $i--) {
+ if ($tokens[$i]['line'] === $tokens[$stackPtr]['line']) {
+ continue;
+ }
+
+ // We changed lines.
+ if ($tokens[($i + 1)]['code'] === T_WHITESPACE) {
+ $classIndent = strlen($tokens[($i + 1)]['content']);
+ }
+
+ break;
+ }
+
+ $className = $phpcsFile->findNext(T_STRING, $stackPtr);
+
+ // Spacing of the keyword.
+ $gap = $tokens[($stackPtr + 1)]['content'];
+ if (strlen($gap) !== 1) {
+ $found = strlen($gap);
+ $error = 'Expected 1 space between %s keyword and %s name; %s found';
+ $data = array(
+ $stackPtrType,
+ $stackPtrType,
+ $found,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterKeyword', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+
+ // Check after the class/interface name.
+ if ($tokens[($className + 2)]['line'] === $tokens[$className]['line']) {
+ $gap = $tokens[($className + 1)]['content'];
+ if (strlen($gap) !== 1) {
+ $found = strlen($gap);
+ $error = 'Expected 1 space after %s name; %s found';
+ $data = array(
+ $stackPtrType,
+ $found,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $className, 'SpaceAfterName', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($className + 1), ' ');
+ }
+ }
+ }
+
+ $openingBrace = $tokens[$stackPtr]['scope_opener'];
+
+ // Check positions of the extends and implements keywords.
+ foreach (array('extends', 'implements') as $keywordType) {
+ $keyword = $phpcsFile->findNext(constant('T_'.strtoupper($keywordType)), ($stackPtr + 1), $openingBrace);
+ if ($keyword !== false) {
+ if ($tokens[$keyword]['line'] !== $tokens[$stackPtr]['line']) {
+ $error = 'The '.$keywordType.' keyword must be on the same line as the %s name';
+ $data = array($stackPtrType);
+ $fix = $phpcsFile->addFixableError($error, $keyword, ucfirst($keywordType).'Line', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($stackPtr + 1); $i < $keyword; $i++) {
+ if ($tokens[$i]['line'] !== $tokens[($i + 1)]['line']) {
+ $phpcsFile->fixer->substrToken($i, 0, (strlen($phpcsFile->eolChar) * -1));
+ }
+ }
+
+ $phpcsFile->fixer->addContentBefore($keyword, ' ');
+ $phpcsFile->fixer->endChangeset();
+ }
+ } else {
+ // Check the whitespace before. Whitespace after is checked
+ // later by looking at the whitespace before the first class name
+ // in the list.
+ $gap = strlen($tokens[($keyword - 1)]['content']);
+ if ($gap !== 1) {
+ $error = 'Expected 1 space before '.$keywordType.' keyword; %s found';
+ $data = array($gap);
+ $fix = $phpcsFile->addFixableError($error, $keyword, 'SpaceBefore'.ucfirst($keywordType), $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($keyword - 1), ' ');
+ }
+ }
+ }//end if
+ }//end if
+ }//end foreach
+
+ // Check each of the extends/implements class names. If the extends/implements
+ // keyword is the last content on the line, it means we need to check for
+ // the multi-line format, so we do not include the class names
+ // from the extends/implements list in the following check.
+ // Note that classes can only extend one other class, so they can't use a
+ // multi-line extends format, whereas an interface can extend multiple
+ // other interfaces, and so uses a multi-line extends format.
+ if ($tokens[$stackPtr]['code'] === T_INTERFACE) {
+ $keywordTokenType = T_EXTENDS;
+ } else {
+ $keywordTokenType = T_IMPLEMENTS;
+ }
+
+ $implements = $phpcsFile->findNext($keywordTokenType, ($stackPtr + 1), $openingBrace);
+ $multiLineImplements = false;
+ if ($implements !== false) {
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($openingBrace - 1), $implements, true);
+ if ($tokens[$prev]['line'] !== $tokens[$implements]['line']) {
+ $multiLineImplements = true;
+ }
+ }
+
+ $find = array(
+ T_STRING,
+ $keywordTokenType,
+ );
+
+ $classNames = array();
+ $nextClass = $phpcsFile->findNext($find, ($className + 2), ($openingBrace - 1));
+ while ($nextClass !== false) {
+ $classNames[] = $nextClass;
+ $nextClass = $phpcsFile->findNext($find, ($nextClass + 1), ($openingBrace - 1));
+ }
+
+ $classCount = count($classNames);
+ $checkingImplements = false;
+ $implementsToken = null;
+ foreach ($classNames as $i => $className) {
+ if ($tokens[$className]['code'] === $keywordTokenType) {
+ $checkingImplements = true;
+ $implementsToken = $className;
+ continue;
+ }
+
+ if ($checkingImplements === true
+ && $multiLineImplements === true
+ && ($tokens[($className - 1)]['code'] !== T_NS_SEPARATOR
+ || $tokens[($className - 2)]['code'] !== T_STRING)
+ ) {
+ $prev = $phpcsFile->findPrevious(
+ array(
+ T_NS_SEPARATOR,
+ T_WHITESPACE,
+ ),
+ ($className - 1),
+ $implements,
+ true
+ );
+
+ if ($prev === $implementsToken && $tokens[$className]['line'] !== ($tokens[$prev]['line'] + 1)) {
+ if ($keywordTokenType === T_EXTENDS) {
+ $error = 'The first item in a multi-line extends list must be on the line following the extends keyword';
+ $fix = $phpcsFile->addFixableError($error, $className, 'FirstExtendsInterfaceSameLine');
+ } else {
+ $error = 'The first item in a multi-line implements list must be on the line following the implements keyword';
+ $fix = $phpcsFile->addFixableError($error, $className, 'FirstInterfaceSameLine');
+ }
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($prev + 1); $i < $className; $i++) {
+ if ($tokens[$i]['code'] !== T_WHITESPACE) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->addNewline($prev);
+ $phpcsFile->fixer->endChangeset();
+ }
+ } else if ($tokens[$prev]['line'] !== ($tokens[$className]['line'] - 1)) {
+ if ($keywordTokenType === T_EXTENDS) {
+ $error = 'Only one interface may be specified per line in a multi-line extends declaration';
+ $fix = $phpcsFile->addFixableError($error, $className, 'ExtendsInterfaceSameLine');
+ } else {
+ $error = 'Only one interface may be specified per line in a multi-line implements declaration';
+ $fix = $phpcsFile->addFixableError($error, $className, 'InterfaceSameLine');
+ }
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($prev + 1); $i < $className; $i++) {
+ if ($tokens[$i]['code'] !== T_WHITESPACE) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->addNewline($prev);
+ $phpcsFile->fixer->endChangeset();
+ }
+ } else {
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($className - 1), $implements);
+ if ($tokens[$prev]['line'] !== $tokens[$className]['line']) {
+ $found = 0;
+ } else {
+ $found = strlen($tokens[$prev]['content']);
+ }
+
+ $expected = ($classIndent + $this->indent);
+ if ($found !== $expected) {
+ $error = 'Expected %s spaces before interface name; %s found';
+ $data = array(
+ $expected,
+ $found,
+ );
+ $fix = $phpcsFile->addFixableError($error, $className, 'InterfaceWrongIndent', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $expected);
+ if ($found === 0) {
+ $phpcsFile->fixer->addContent($prev, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken($prev, $padding);
+ }
+ }
+ }
+ }//end if
+ } else if ($tokens[($className - 1)]['code'] !== T_NS_SEPARATOR
+ || $tokens[($className - 2)]['code'] !== T_STRING
+ ) {
+ // Not part of a longer fully qualified class name.
+ if ($tokens[($className - 1)]['code'] === T_COMMA
+ || ($tokens[($className - 1)]['code'] === T_NS_SEPARATOR
+ && $tokens[($className - 2)]['code'] === T_COMMA)
+ ) {
+ $error = 'Expected 1 space before "%s"; 0 found';
+ $data = array($tokens[$className]['content']);
+ $fix = $phpcsFile->addFixableError($error, ($nextComma + 1), 'NoSpaceBeforeName', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->addContentBefore(($nextComma + 1), ' ');
+ }
+ } else {
+ if ($tokens[($className - 1)]['code'] === T_NS_SEPARATOR) {
+ $prev = ($className - 2);
+ } else {
+ $prev = ($className - 1);
+ }
+
+ $spaceBefore = strlen($tokens[$prev]['content']);
+ if ($spaceBefore !== 1) {
+ $error = 'Expected 1 space before "%s"; %s found';
+ $data = array(
+ $tokens[$className]['content'],
+ $spaceBefore,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $className, 'SpaceBeforeName', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($prev, ' ');
+ }
+ }
+ }//end if
+ }//end if
+
+ if ($checkingImplements === true
+ && $tokens[($className + 1)]['code'] !== T_NS_SEPARATOR
+ && $tokens[($className + 1)]['code'] !== T_COMMA
+ ) {
+ if ($i !== ($classCount - 1)) {
+ // This is not the last class name, and the comma
+ // is not where we expect it to be.
+ if ($tokens[($className + 2)]['code'] !== $keywordTokenType) {
+ $error = 'Expected 0 spaces between "%s" and comma; %s found';
+ $data = array(
+ $tokens[$className]['content'],
+ strlen($tokens[($className + 1)]['content']),
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $className, 'SpaceBeforeComma', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($className + 1), '');
+ }
+ }
+ }
+
+ $nextComma = $phpcsFile->findNext(T_COMMA, $className);
+ } else {
+ $nextComma = ($className + 1);
+ }//end if
+ }//end foreach
+
+ }//end processOpen()
+
+
+ /**
+ * Processes the closing section of a class declaration.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function processClose(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Check that the closing brace comes right after the code body.
+ $closeBrace = $tokens[$stackPtr]['scope_closer'];
+ $prevContent = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBrace - 1), null, true);
+ if ($prevContent !== $tokens[$stackPtr]['scope_opener']
+ && $tokens[$prevContent]['line'] !== ($tokens[$closeBrace]['line'] - 1)
+ ) {
+ $error = 'The closing brace for the %s must go on the next line after the body';
+ $data = array($tokens[$stackPtr]['content']);
+ $fix = $phpcsFile->addFixableError($error, $closeBrace, 'CloseBraceAfterBody', $data);
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($prevContent + 1); $i < $closeBrace; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ if (strpos($tokens[$prevContent]['content'], $phpcsFile->eolChar) === false) {
+ $phpcsFile->fixer->replaceToken($closeBrace, $phpcsFile->eolChar.$tokens[$closeBrace]['content']);
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }//end if
+
+ // Check the closing brace is on it's own line, but allow
+ // for comments like "//end class".
+ $nextContent = $phpcsFile->findNext(array(T_WHITESPACE, T_COMMENT), ($closeBrace + 1), null, true);
+ if ($tokens[$nextContent]['content'] !== $phpcsFile->eolChar
+ && $tokens[$nextContent]['line'] === $tokens[$closeBrace]['line']
+ ) {
+ $type = strtolower($tokens[$stackPtr]['content']);
+ $error = 'Closing %s brace must be on a line by itself';
+ $data = array($type);
+ $phpcsFile->addError($error, $closeBrace, 'CloseBraceSameLine', $data);
+ }
+
+ }//end processClose()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that properties are declared correctly.
+ *
+ * @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\Standards\PSR2\Sniffs\Classes;
+
+use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
+use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Files\File;
+
+class PropertyDeclarationSniff extends AbstractVariableSniff
+{
+
+
+ /**
+ * Processes the function tokens within the class.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position where the token was found.
+ *
+ * @return void
+ */
+ protected function processMemberVar(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['content'][1] === '_') {
+ $error = 'Property name "%s" should not be prefixed with an underscore to indicate visibility';
+ $data = array($tokens[$stackPtr]['content']);
+ $phpcsFile->addWarning($error, $stackPtr, 'Underscore', $data);
+ }
+
+ // Detect multiple properties defined at the same time. Throw an error
+ // for this, but also only process the first property in the list so we don't
+ // repeat errors.
+ $find = Tokens::$scopeModifiers;
+ $find = array_merge($find, array(T_VARIABLE, T_VAR, T_SEMICOLON));
+ $prev = $phpcsFile->findPrevious($find, ($stackPtr - 1));
+ if ($tokens[$prev]['code'] === T_VARIABLE) {
+ return;
+ }
+
+ if ($tokens[$prev]['code'] === T_VAR) {
+ $error = 'The var keyword must not be used to declare a property';
+ $phpcsFile->addError($error, $stackPtr, 'VarUsed');
+ }
+
+ $next = $phpcsFile->findNext(array(T_VARIABLE, T_SEMICOLON), ($stackPtr + 1));
+ if ($tokens[$next]['code'] === T_VARIABLE) {
+ $error = 'There must not be more than one property declared per statement';
+ $phpcsFile->addError($error, $stackPtr, 'Multiple');
+ }
+
+ $modifier = $phpcsFile->findPrevious(Tokens::$scopeModifiers, $stackPtr);
+ if (($modifier === false) || ($tokens[$modifier]['line'] !== $tokens[$stackPtr]['line'])) {
+ $error = 'Visibility must be declared on property "%s"';
+ $data = array($tokens[$stackPtr]['content']);
+ $phpcsFile->addError($error, $stackPtr, 'ScopeMissing', $data);
+ }
+
+ }//end processMemberVar()
+
+
+ /**
+ * Processes normal variables.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position where the token was found.
+ *
+ * @return void
+ */
+ protected function processVariable(File $phpcsFile, $stackPtr)
+ {
+ /*
+ We don't care about normal variables.
+ */
+
+ }//end processVariable()
+
+
+ /**
+ * Processes variables in double quoted strings.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position where the token was found.
+ *
+ * @return void
+ */
+ protected function processVariableInString(File $phpcsFile, $stackPtr)
+ {
+ /*
+ We don't care about normal variables.
+ */
+
+ }//end processVariableInString()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that control structures have the correct spacing around brackets.
+ *
+ * @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\Standards\PSR2\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ControlStructureSpacingSniff implements Sniff
+{
+
+
+ /**
+ * How many spaces should follow the opening bracket.
+ *
+ * @var integer
+ */
+ public $requiredSpacesAfterOpen = 0;
+
+ /**
+ * How many spaces should precede the closing bracket.
+ *
+ * @var integer
+ */
+ public $requiredSpacesBeforeClose = 0;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_IF,
+ T_WHILE,
+ T_FOREACH,
+ T_FOR,
+ T_SWITCH,
+ T_DO,
+ T_ELSE,
+ T_ELSEIF,
+ T_TRY,
+ T_CATCH,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $this->requiredSpacesAfterOpen = (int) $this->requiredSpacesAfterOpen;
+ $this->requiredSpacesBeforeClose = (int) $this->requiredSpacesBeforeClose;
+ $tokens = $phpcsFile->getTokens();
+
+ if (isset($tokens[$stackPtr]['parenthesis_opener']) === false
+ || isset($tokens[$stackPtr]['parenthesis_closer']) === false
+ ) {
+ return;
+ }
+
+ $parenOpener = $tokens[$stackPtr]['parenthesis_opener'];
+ $parenCloser = $tokens[$stackPtr]['parenthesis_closer'];
+ $spaceAfterOpen = 0;
+ if ($tokens[($parenOpener + 1)]['code'] === T_WHITESPACE) {
+ if (strpos($tokens[($parenOpener + 1)]['content'], $phpcsFile->eolChar) !== false) {
+ $spaceAfterOpen = 'newline';
+ } else {
+ $spaceAfterOpen = strlen($tokens[($parenOpener + 1)]['content']);
+ }
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Spaces after control structure open parenthesis', $spaceAfterOpen);
+
+ if ($spaceAfterOpen !== $this->requiredSpacesAfterOpen) {
+ $error = 'Expected %s spaces after opening bracket; %s found';
+ $data = array(
+ $this->requiredSpacesAfterOpen,
+ $spaceAfterOpen,
+ );
+ $fix = $phpcsFile->addFixableError($error, ($parenOpener + 1), 'SpacingAfterOpenBrace', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $this->requiredSpacesAfterOpen);
+ if ($spaceAfterOpen === 0) {
+ $phpcsFile->fixer->addContent($parenOpener, $padding);
+ } else if ($spaceAfterOpen === 'newline') {
+ $phpcsFile->fixer->replaceToken(($parenOpener + 1), '');
+ } else {
+ $phpcsFile->fixer->replaceToken(($parenOpener + 1), $padding);
+ }
+ }
+ }
+
+ if ($tokens[$parenOpener]['line'] === $tokens[$parenCloser]['line']) {
+ $spaceBeforeClose = 0;
+ if ($tokens[($parenCloser - 1)]['code'] === T_WHITESPACE) {
+ $spaceBeforeClose = strlen(ltrim($tokens[($parenCloser - 1)]['content'], $phpcsFile->eolChar));
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Spaces before control structure close parenthesis', $spaceBeforeClose);
+
+ if ($spaceBeforeClose !== $this->requiredSpacesBeforeClose) {
+ $error = 'Expected %s spaces before closing bracket; %s found';
+ $data = array(
+ $this->requiredSpacesBeforeClose,
+ $spaceBeforeClose,
+ );
+ $fix = $phpcsFile->addFixableError($error, ($parenCloser - 1), 'SpaceBeforeCloseBrace', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $this->requiredSpacesBeforeClose);
+ if ($spaceBeforeClose === 0) {
+ $phpcsFile->fixer->addContentBefore($parenCloser, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($parenCloser - 1), $padding);
+ }
+ }
+ }
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that there are no else if statements (elseif should be used instead).
+ *
+ * @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\Standards\PSR2\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ElseIfDeclarationSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_ELSE,
+ T_ELSEIF,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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_ELSEIF) {
+ $phpcsFile->recordMetric($stackPtr, 'Use of ELSE IF or ELSEIF', 'elseif');
+ return;
+ }
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$next]['code'] === T_IF) {
+ $phpcsFile->recordMetric($stackPtr, 'Use of ELSE IF or ELSEIF', 'else if');
+ $error = 'Usage of ELSE IF is discouraged; use ELSEIF instead';
+ $fix = $phpcsFile->addFixableWarning($error, $stackPtr, 'NotAllowed');
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken($stackPtr, 'elseif');
+ for ($i = ($stackPtr + 1); $i <= $next; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures all switch statements are defined correctly.
+ *
+ * @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\Standards\PSR2\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class SwitchDeclarationSniff implements Sniff
+{
+
+ /**
+ * The number of spaces code should be indented.
+ *
+ * @var integer
+ */
+ public $indent = 4;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_SWITCH);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // We can't process SWITCH statements unless we know where they start and end.
+ if (isset($tokens[$stackPtr]['scope_opener']) === false
+ || isset($tokens[$stackPtr]['scope_closer']) === false
+ ) {
+ return;
+ }
+
+ $switch = $tokens[$stackPtr];
+ $nextCase = $stackPtr;
+ $caseAlignment = ($switch['column'] + $this->indent);
+ $caseCount = 0;
+ $foundDefault = false;
+
+ while (($nextCase = $this->findNextCase($phpcsFile, ($nextCase + 1), $switch['scope_closer'])) !== false) {
+ if ($tokens[$nextCase]['code'] === T_DEFAULT) {
+ $type = 'default';
+ $foundDefault = true;
+ } else {
+ $type = 'case';
+ $caseCount++;
+ }
+
+ if ($tokens[$nextCase]['content'] !== strtolower($tokens[$nextCase]['content'])) {
+ $expected = strtolower($tokens[$nextCase]['content']);
+ $error = strtoupper($type).' keyword must be lowercase; expected "%s" but found "%s"';
+ $data = array(
+ $expected,
+ $tokens[$nextCase]['content'],
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $nextCase, $type.'NotLower', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($nextCase, $expected);
+ }
+ }
+
+ if ($type === 'case'
+ && ($tokens[($nextCase + 1)]['code'] !== T_WHITESPACE
+ || $tokens[($nextCase + 1)]['content'] !== ' ')
+ ) {
+ $error = 'CASE keyword must be followed by a single space';
+ $fix = $phpcsFile->addFixableError($error, $nextCase, 'SpacingAfterCase');
+ if ($fix === true) {
+ if ($tokens[($nextCase + 1)]['code'] !== T_WHITESPACE) {
+ $phpcsFile->fixer->addContent($nextCase, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($nextCase + 1), ' ');
+ }
+ }
+ }
+
+ $opener = $tokens[$nextCase]['scope_opener'];
+ $nextCloser = $tokens[$nextCase]['scope_closer'];
+ if ($tokens[$opener]['code'] === T_COLON) {
+ if ($tokens[($opener - 1)]['code'] === T_WHITESPACE) {
+ $error = 'There must be no space before the colon in a '.strtoupper($type).' statement';
+ $fix = $phpcsFile->addFixableError($error, $nextCase, 'SpaceBeforeColon'.strtoupper($type));
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($opener - 1), '');
+ }
+ }
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($opener + 1), null, true);
+ if ($tokens[$next]['line'] === $tokens[$opener]['line']
+ && $tokens[$next]['code'] === T_COMMENT
+ ) {
+ // Skip comments on the same line.
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($next + 1), null, true);
+ }
+
+ if ($tokens[$next]['line'] !== ($tokens[$opener]['line'] + 1)) {
+ $error = 'The '.strtoupper($type).' body must start on the line following the statement';
+ $fix = $phpcsFile->addFixableError($error, $nextCase, 'BodyOnNextLine'.strtoupper($type));
+ if ($fix === true) {
+ if ($tokens[$next]['line'] === $tokens[$opener]['line']) {
+ $padding = str_repeat(' ', ($caseAlignment + $this->indent - 1));
+ $phpcsFile->fixer->addContentBefore($next, $phpcsFile->eolChar.$padding);
+ } else {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($opener + 1); $i < $next; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$next]['line']) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->addNewLineBefore($i);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ }//end if
+
+ if ($tokens[$nextCloser]['scope_condition'] === $nextCase) {
+ // Only need to check some things once, even if the
+ // closer is shared between multiple case statements, or even
+ // the default case.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($nextCloser - 1), $nextCase, true);
+ if ($tokens[$prev]['line'] === $tokens[$nextCloser]['line']) {
+ $error = 'Terminating statement must be on a line by itself';
+ $fix = $phpcsFile->addFixableError($error, $nextCloser, 'BreakNotNewLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewLine($prev);
+ $phpcsFile->fixer->replaceToken($nextCloser, trim($tokens[$nextCloser]['content']));
+ }
+ } else {
+ $diff = ($caseAlignment + $this->indent - $tokens[$nextCloser]['column']);
+ if ($diff !== 0) {
+ $error = 'Terminating statement must be indented to the same level as the CASE body';
+ $fix = $phpcsFile->addFixableError($error, $nextCloser, 'BreakIndent');
+ if ($fix === true) {
+ if ($diff > 0) {
+ $phpcsFile->fixer->addContentBefore($nextCloser, str_repeat(' ', $diff));
+ } else {
+ $phpcsFile->fixer->substrToken(($nextCloser - 1), 0, $diff);
+ }
+ }
+ }
+ }//end if
+ }//end if
+ } else {
+ $error = strtoupper($type).' statements must be defined using a colon';
+ $phpcsFile->addError($error, $nextCase, 'WrongOpener'.$type);
+ }//end if
+
+ // We only want cases from here on in.
+ if ($type !== 'case') {
+ continue;
+ }
+
+ $nextCode = $phpcsFile->findNext(T_WHITESPACE, ($opener + 1), $nextCloser, true);
+
+ if ($tokens[$nextCode]['code'] !== T_CASE && $tokens[$nextCode]['code'] !== T_DEFAULT) {
+ // This case statement has content. If the next case or default comes
+ // before the closer, it means we don't have an obvious terminating
+ // statement and need to make some more effort to find one. If we
+ // don't, we do need a comment.
+ $nextCode = $this->findNextCase($phpcsFile, ($opener + 1), $nextCloser);
+ if ($nextCode !== false) {
+ $prevCode = $phpcsFile->findPrevious(T_WHITESPACE, ($nextCode - 1), $nextCase, true);
+ if ($tokens[$prevCode]['code'] !== T_COMMENT
+ && $this->findNestedTerminator($phpcsFile, ($opener + 1), $nextCode) === false
+ ) {
+ $error = 'There must be a comment when fall-through is intentional in a non-empty case body';
+ $phpcsFile->addError($error, $nextCase, 'TerminatingComment');
+ }
+ }
+ }
+ }//end while
+
+ }//end process()
+
+
+ /**
+ * Find the next CASE or DEFAULT statement from a point in the file.
+ *
+ * Note that nested switches are ignored.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position to start looking at.
+ * @param int $end The position to stop looking at.
+ *
+ * @return int | bool
+ */
+ private function findNextCase($phpcsFile, $stackPtr, $end)
+ {
+ $tokens = $phpcsFile->getTokens();
+ while (($stackPtr = $phpcsFile->findNext(array(T_CASE, T_DEFAULT, T_SWITCH), $stackPtr, $end)) !== false) {
+ // Skip nested SWITCH statements; they are handled on their own.
+ if ($tokens[$stackPtr]['code'] === T_SWITCH) {
+ $stackPtr = $tokens[$stackPtr]['scope_closer'];
+ continue;
+ }
+
+ break;
+ }
+
+ return $stackPtr;
+
+ }//end findNextCase()
+
+
+ /**
+ * Returns true if a nested terminating statement is found.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position to start looking at.
+ * @param int $end The position to stop looking at.
+ *
+ * @return bool
+ */
+ private function findNestedTerminator($phpcsFile, $stackPtr, $end)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $terminators = array(
+ T_RETURN,
+ T_BREAK,
+ T_CONTINUE,
+ T_THROW,
+ T_EXIT,
+ );
+
+ $lastToken = $phpcsFile->findPrevious(T_WHITESPACE, ($end - 1), $stackPtr, true);
+ if ($lastToken !== false) {
+ if ($tokens[$lastToken]['code'] === T_CLOSE_CURLY_BRACKET) {
+ // We found a closing curly bracket and want to check if its
+ // block belongs to an IF, ELSEIF or ELSE clause. If yes, we
+ // continue searching for a terminating statement within that
+ // block. Note that we have to make sure that every block of
+ // the entire if/else statement has a terminating statement.
+ $currentCloser = $lastToken;
+ $hasElseBlock = false;
+ do {
+ $scopeOpener = $tokens[$currentCloser]['scope_opener'];
+ $scopeCloser = $tokens[$currentCloser]['scope_closer'];
+
+ $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, ($scopeOpener - 1), $stackPtr, true);
+ if ($prevToken === false) {
+ return false;
+ }
+
+ // IF and ELSEIF clauses possess a condition we have to account for.
+ if ($tokens[$prevToken]['code'] === T_CLOSE_PARENTHESIS) {
+ $prevToken = $tokens[$prevToken]['parenthesis_owner'];
+ }
+
+ if ($tokens[$prevToken]['code'] === T_IF) {
+ // If we have not encountered an ELSE clause by now, we cannot
+ // be sure that the whole statement terminates in every case.
+ if ($hasElseBlock === false) {
+ return false;
+ }
+
+ return $this->findNestedTerminator($phpcsFile, ($scopeOpener + 1), $scopeCloser);
+ } else if ($tokens[$prevToken]['code'] === T_ELSEIF
+ || $tokens[$prevToken]['code'] === T_ELSE
+ ) {
+ // If we find a terminating statement within this block,
+ // we continue with the previous ELSEIF or IF clause.
+ $hasTerminator = $this->findNestedTerminator($phpcsFile, ($scopeOpener + 1), $scopeCloser);
+ if ($hasTerminator === false) {
+ return false;
+ }
+
+ $currentCloser = $phpcsFile->findPrevious(T_WHITESPACE, ($prevToken - 1), $stackPtr, true);
+ if ($tokens[$prevToken]['code'] === T_ELSE) {
+ $hasElseBlock = true;
+ }
+ } else {
+ return false;
+ }//end if
+ } while ($currentCloser !== false && $tokens[$currentCloser]['code'] === T_CLOSE_CURLY_BRACKET);
+
+ return true;
+ } else if ($tokens[$lastToken]['code'] === T_SEMICOLON) {
+ // We found the last statement of the CASE. Now we want to
+ // check whether it is a terminating one.
+ $terminator = $phpcsFile->findStartOfStatement(($lastToken - 1));
+ if (in_array($tokens[$terminator]['code'], $terminators, true) === true) {
+ return $terminator;
+ }
+ }//end if
+ }//end if
+
+ return false;
+
+ }//end findNestedTerminator()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that the file does not end with a closing tag.
+ *
+ * @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\Standards\PSR2\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ClosingTagSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // Make sure this file only contains PHP code.
+ for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
+ if ($tokens[$i]['code'] === T_INLINE_HTML
+ && trim($tokens[$i]['content']) !== ''
+ ) {
+ return $phpcsFile->numTokens;
+ }
+ }
+
+ // Find the last non-empty token.
+ for ($last = ($phpcsFile->numTokens - 1); $last > 0; $last--) {
+ if (trim($tokens[$last]['content']) !== '') {
+ break;
+ }
+ }
+
+ if ($tokens[$last]['code'] === T_CLOSE_TAG) {
+ $error = 'A closing tag is not permitted at the end of a PHP file';
+ $fix = $phpcsFile->addFixableError($error, $last, 'NotAllowed');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken($last, $phpcsFile->eolChar);
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($last - 1), null, true);
+ if ($tokens[$prev]['code'] !== T_SEMICOLON
+ && $tokens[$prev]['code'] !== T_CLOSE_CURLY_BRACKET
+ ) {
+ $phpcsFile->fixer->addContent($prev, ';');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'PHP closing tag at end of PHP-only file', 'yes');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'PHP closing tag at end of PHP-only file', 'no');
+ }
+
+ // Ignore the rest of the file.
+ return $phpcsFile->numTokens;
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures the file ends with a newline character.
+ *
+ * @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\Standards\PSR2\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class EndFileNewlineSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ if ($phpcsFile->findNext(T_INLINE_HTML, ($stackPtr + 1)) !== false) {
+ return ($phpcsFile->numTokens + 1);
+ }
+
+ // Skip to the end of the file.
+ $tokens = $phpcsFile->getTokens();
+ $lastToken = ($phpcsFile->numTokens - 1);
+
+ if ($tokens[$lastToken]['content'] === '') {
+ $lastToken--;
+ }
+
+ // Hard-coding the expected \n in this sniff as it is PSR-2 specific and
+ // PSR-2 enforces the use of unix style newlines.
+ if (substr($tokens[$lastToken]['content'], -1) !== "\n") {
+ $error = 'Expected 1 newline at end of file; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $lastToken, 'NoneFound');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewline($lastToken);
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Number of newlines at EOF', '0');
+ return ($phpcsFile->numTokens + 1);
+ }
+
+ // Go looking for the last non-empty line.
+ $lastLine = $tokens[$lastToken]['line'];
+ if ($tokens[$lastToken]['code'] === T_WHITESPACE
+ || $tokens[$lastToken]['code'] === T_DOC_COMMENT_WHITESPACE
+ ) {
+ $lastCode = $phpcsFile->findPrevious(array(T_WHITESPACE, T_DOC_COMMENT_WHITESPACE), ($lastToken - 1), null, true);
+ } else {
+ $lastCode = $lastToken;
+ }
+
+ $lastCodeLine = $tokens[$lastCode]['line'];
+ $blankLines = ($lastLine - $lastCodeLine + 1);
+ $phpcsFile->recordMetric($stackPtr, 'Number of newlines at EOF', $blankLines);
+
+ if ($blankLines > 1) {
+ $error = 'Expected 1 blank line at end of file; %s found';
+ $data = array($blankLines);
+ $fix = $phpcsFile->addFixableError($error, $lastCode, 'TooMany', $data);
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken($lastCode, rtrim($tokens[$lastCode]['content']));
+ for ($i = ($lastCode + 1); $i < $lastToken; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->replaceToken($lastToken, $phpcsFile->eolChar);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ // Skip the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that the function call format is correct.
+ *
+ * @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\Standards\PSR2\Sniffs\Methods;
+
+use PHP_CodeSniffer\Standards\PEAR\Sniffs\Functions\FunctionCallSignatureSniff as PEARFunctionCallSignatureSniff;
+use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Files\File;
+
+class FunctionCallSignatureSniff extends PEARFunctionCallSignatureSniff
+{
+
+ /**
+ * If TRUE, multiple arguments can be defined per line in a multi-line call.
+ *
+ * @var boolean
+ */
+ public $allowMultipleArguments = false;
+
+
+ /**
+ * Processes single-line calls.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $openBracket The position of the opening bracket
+ * in the stack passed in $tokens.
+ * @param array $tokens The stack of tokens that make up
+ * the file.
+ *
+ * @return void
+ */
+ public function isMultiLineCall(File $phpcsFile, $stackPtr, $openBracket, $tokens)
+ {
+ // If the first argument is on a new line, this is a multi-line
+ // function call, even if there is only one argument.
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($openBracket + 1), null, true);
+ if ($tokens[$next]['line'] !== $tokens[$stackPtr]['line']) {
+ return true;
+ }
+
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+
+ $end = $phpcsFile->findEndOfStatement($openBracket + 1);
+ while ($tokens[$end]['code'] === T_COMMA) {
+ // If the next bit of code is not on the same line, this is a
+ // multi-line function call.
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), $closeBracket, true);
+ if ($next === false) {
+ return false;
+ }
+
+ if ($tokens[$next]['line'] !== $tokens[$end]['line']) {
+ return true;
+ }
+
+ $end = $phpcsFile->findEndOfStatement($next);
+ }
+
+ // We've reached the last argument, so see if the next content
+ // (should be the close bracket) is also on the same line.
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), $closeBracket, true);
+ if ($next !== false && $tokens[$next]['line'] !== $tokens[$end]['line']) {
+ return true;
+ }
+
+ return false;
+
+ }//end isMultiLineCall()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that the closing brace of a function goes directly after the body.
+ *
+ * @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\Standards\PSR2\Sniffs\Methods;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class FunctionClosingBraceSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_FUNCTION,
+ T_CLOSURE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ // Probably an interface method.
+ return;
+ }
+
+ $closeBrace = $tokens[$stackPtr]['scope_closer'];
+ $prevContent = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBrace - 1), null, true);
+ $found = ($tokens[$closeBrace]['line'] - $tokens[$prevContent]['line'] - 1);
+
+ if ($found < 0) {
+ // Brace isn't on a new line, so not handled by us.
+ return;
+ }
+
+ if ($found === 0) {
+ // All is good.
+ return;
+ }
+
+ $error = 'Function closing brace must go on the next line following the body; found %s blank lines before brace';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpacingBeforeClose', $data);
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($prevContent + 1); $i < $closeBrace; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$prevContent]['line']) {
+ continue;
+ }
+
+ // Don't remove any identation before the brace.
+ if ($tokens[$i]['line'] === $tokens[$closeBrace]['line']) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that the method declaration is correct.
+ *
+ * @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\Standards\PSR2\Sniffs\Methods;
+
+use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
+use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Files\File;
+
+class MethodDeclarationSniff extends AbstractScopeSniff
+{
+
+
+ /**
+ * Constructs a Squiz_Sniffs_Scope_MethodScopeSniff.
+ */
+ public function __construct()
+ {
+ parent::__construct(array(T_CLASS, T_INTERFACE), array(T_FUNCTION));
+
+ }//end __construct()
+
+
+ /**
+ * Processes the function tokens within the class.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The 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
+ */
+ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $methodName = $phpcsFile->getDeclarationName($stackPtr);
+ if ($methodName === null) {
+ // Ignore closures.
+ return;
+ }
+
+ if ($methodName[0] === '_' && isset($methodName[1]) === true && $methodName[1] !== '_') {
+ $error = 'Method name "%s" should not be prefixed with an underscore to indicate visibility';
+ $data = array($methodName);
+ $phpcsFile->addWarning($error, $stackPtr, 'Underscore', $data);
+ }
+
+ $visibility = 0;
+ $static = 0;
+ $abstract = 0;
+ $final = 0;
+
+ $find = Tokens::$methodPrefixes;
+ $find[] = T_WHITESPACE;
+ $prev = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
+
+ $prefix = $stackPtr;
+ while (($prefix = $phpcsFile->findPrevious(Tokens::$methodPrefixes, ($prefix - 1), $prev)) !== false) {
+ switch ($tokens[$prefix]['code']) {
+ case T_STATIC:
+ $static = $prefix;
+ break;
+ case T_ABSTRACT:
+ $abstract = $prefix;
+ break;
+ case T_FINAL:
+ $final = $prefix;
+ break;
+ default:
+ $visibility = $prefix;
+ break;
+ }
+ }
+
+ $fixes = array();
+
+ if ($visibility !== 0 && $final > $visibility) {
+ $error = 'The final declaration must precede the visibility declaration';
+ $fix = $phpcsFile->addFixableError($error, $final, 'FinalAfterVisibility');
+ if ($fix === true) {
+ $fixes[$final] = '';
+ $fixes[($final + 1)] = '';
+ if (isset($fixes[$visibility]) === true) {
+ $fixes[$visibility] = 'final '.$fixes[$visibility];
+ } else {
+ $fixes[$visibility] = 'final '.$tokens[$visibility]['content'];
+ }
+ }
+ }
+
+ if ($visibility !== 0 && $abstract > $visibility) {
+ $error = 'The abstract declaration must precede the visibility declaration';
+ $fix = $phpcsFile->addFixableError($error, $abstract, 'AbstractAfterVisibility');
+ if ($fix === true) {
+ $fixes[$abstract] = '';
+ $fixes[($abstract + 1)] = '';
+ if (isset($fixes[$visibility]) === true) {
+ $fixes[$visibility] = 'abstract '.$fixes[$visibility];
+ } else {
+ $fixes[$visibility] = 'abstract '.$tokens[$visibility]['content'];
+ }
+ }
+ }
+
+ if ($static !== 0 && $static < $visibility) {
+ $error = 'The static declaration must come after the visibility declaration';
+ $fix = $phpcsFile->addFixableError($error, $static, 'StaticBeforeVisibility');
+ if ($fix === true) {
+ $fixes[$static] = '';
+ $fixes[($static + 1)] = '';
+ if (isset($fixes[$visibility]) === true) {
+ $fixes[$visibility] = $fixes[$visibility].' static';
+ } else {
+ $fixes[$visibility] = $tokens[$visibility]['content'].' static';
+ }
+ }
+ }
+
+ // Batch all the fixes together to reduce the possibility of conflicts.
+ if (empty($fixes) === false) {
+ $phpcsFile->fixer->beginChangeset();
+ foreach ($fixes as $stackPtr => $content) {
+ $phpcsFile->fixer->replaceToken($stackPtr, $content);
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ }//end processTokenWithinScope()
+
+
+ /**
+ * 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.
+ *
+ * @return void
+ */
+ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
+ {
+
+ }//end processTokenOutsideScope()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures namespaces are declared correctly.
+ *
+ * @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\Standards\PSR2\Sniffs\Namespaces;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class NamespaceDeclarationSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_NAMESPACE);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ for ($i = ($stackPtr + 1); $i < ($phpcsFile->numTokens - 1); $i++) {
+ if ($tokens[$i]['line'] === $tokens[$stackPtr]['line']) {
+ continue;
+ }
+
+ break;
+ }
+
+ // The $i var now points to the first token on the line after the
+ // namespace declaration, which must be a blank line.
+ $next = $phpcsFile->findNext(T_WHITESPACE, $i, $phpcsFile->numTokens, true);
+ if ($next === false) {
+ return;
+ }
+
+ $diff = ($tokens[$next]['line'] - $tokens[$i]['line']);
+ if ($diff === 1) {
+ return;
+ }
+
+ if ($diff < 0) {
+ $diff = 0;
+ }
+
+ $error = 'There must be one blank line after the namespace declaration';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'BlankLineAfter');
+
+ if ($fix === true) {
+ if ($diff === 0) {
+ $phpcsFile->fixer->addNewlineBefore($i);
+ } else {
+ $phpcsFile->fixer->beginChangeset();
+ for ($x = $i; $x < $next; $x++) {
+ if ($tokens[$x]['line'] === $tokens[$next]['line']) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($x, '');
+ }
+
+ $phpcsFile->fixer->addNewline($i);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures USE blocks are declared correctly.
+ *
+ * @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\Standards\PSR2\Sniffs\Namespaces;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class UseDeclarationSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_USE);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ if ($this->shouldIgnoreUse($phpcsFile, $stackPtr) === true) {
+ return;
+ }
+
+ $tokens = $phpcsFile->getTokens();
+
+ // One space after the use keyword.
+ if ($tokens[($stackPtr + 1)]['content'] !== ' ') {
+ $error = 'There must be a single space after the USE keyword';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterUse');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+
+ // Only one USE declaration allowed per statement.
+ $next = $phpcsFile->findNext(array(T_COMMA, T_SEMICOLON, T_OPEN_USE_GROUP, T_CLOSE_TAG), ($stackPtr + 1));
+ if ($next !== false
+ && $tokens[$next]['code'] !== T_SEMICOLON
+ && $tokens[$next]['code'] !== T_CLOSE_TAG
+ ) {
+ $error = 'There must be one USE keyword per declaration';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'MultipleDeclarations');
+ if ($fix === true) {
+ if ($tokens[$next]['code'] === T_COMMA) {
+ $phpcsFile->fixer->replaceToken($next, ';'.$phpcsFile->eolChar.'use ');
+ } else {
+ $baseUse = rtrim($phpcsFile->getTokensAsString($stackPtr, ($next - $stackPtr)));
+ $closingCurly = $phpcsFile->findNext(T_CLOSE_USE_GROUP, ($next + 1));
+
+ $phpcsFile->fixer->beginChangeset();
+
+ // Remove base use statement.
+ for ($i = $stackPtr; $i <= $next; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ // Convert grouped use statements into full use statements.
+ do {
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), $closingCurly, true);
+
+ $whitespace = $phpcsFile->findPrevious(T_WHITESPACE, ($next - 1), null, true);
+ for ($i = ($whitespace + 1); $i < $next; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ if ($tokens[$next]['code'] === T_CONST || $tokens[$next]['code'] === T_FUNCTION) {
+ $phpcsFile->fixer->addContentBefore($next, 'use ');
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), $closingCurly, true);
+ $phpcsFile->fixer->addContentBefore($next, str_replace('use ', '', $baseUse));
+ } else {
+ $phpcsFile->fixer->addContentBefore($next, $baseUse);
+ }
+
+ $next = $phpcsFile->findNext(T_COMMA, ($next + 1), $closingCurly);
+ if ($next !== false) {
+ $phpcsFile->fixer->replaceToken($next, ';'.$phpcsFile->eolChar);
+ }
+ } while ($next !== false);
+
+ $phpcsFile->fixer->replaceToken($closingCurly, '');
+
+ // Remove any trailing whitespace.
+ $next = $phpcsFile->findNext(T_SEMICOLON, $closingCurly);
+ $whitespace = $phpcsFile->findPrevious(T_WHITESPACE, ($closingCurly - 1), null, true);
+ for ($i = ($whitespace + 1); $i < $next; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+ }//end if
+ }//end if
+
+ // Make sure this USE comes after the first namespace declaration.
+ $prev = $phpcsFile->findPrevious(T_NAMESPACE, ($stackPtr - 1));
+ if ($prev !== false) {
+ $first = $phpcsFile->findNext(T_NAMESPACE, 1);
+ if ($prev !== $first) {
+ $error = 'USE declarations must go after the first namespace declaration';
+ $phpcsFile->addError($error, $stackPtr, 'UseAfterNamespace');
+ }
+ }
+
+ // Only interested in the last USE statement from here onwards.
+ $nextUse = $phpcsFile->findNext(T_USE, ($stackPtr + 1));
+ while ($this->shouldIgnoreUse($phpcsFile, $nextUse) === true) {
+ $nextUse = $phpcsFile->findNext(T_USE, ($nextUse + 1));
+ if ($nextUse === false) {
+ break;
+ }
+ }
+
+ if ($nextUse !== false) {
+ return;
+ }
+
+ $end = $phpcsFile->findNext(T_SEMICOLON, ($stackPtr + 1));
+ if ($end === false) {
+ return;
+ }
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($end + 1), null, true);
+
+ if ($next === false || $tokens[$next]['code'] === T_CLOSE_TAG) {
+ return;
+ }
+
+ $diff = ($tokens[$next]['line'] - $tokens[$end]['line'] - 1);
+ if ($diff !== 1) {
+ if ($diff < 0) {
+ $diff = 0;
+ }
+
+ $error = 'There must be one blank line after the last USE statement; %s found;';
+ $data = array($diff);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterLastUse', $data);
+ if ($fix === true) {
+ if ($diff === 0) {
+ $phpcsFile->fixer->addNewline($end);
+ } else {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($end + 1); $i < $next; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$next]['line']) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->addNewline($end);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ }//end if
+
+ }//end process()
+
+
+ /**
+ * Check if this use statement is part of the namespace block.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ private function shouldIgnoreUse($phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Ignore USE keywords inside closures.
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$next]['code'] === T_OPEN_PARENTHESIS) {
+ return true;
+ }
+
+ // Ignore USE keywords for traits.
+ if ($phpcsFile->hasCondition($stackPtr, array(T_CLASS, T_TRAIT)) === true) {
+ return true;
+ }
+
+ return false;
+
+ }//end shouldIgnoreUse()
+
+
+}//end class
--- /dev/null
+<?php
+class ClassName extends ParentClass implements \ArrayAccess, \Countable
+{
+ // constants, properties, methods
+}
+
+class ClassName extends ParentClass,AnotherParentClass implements \ArrayAccess,\Countable {
+ // constants, properties, methods
+}
+
+class ClassName
+extends ParentClass
+implements \ArrayAccess, \Countable
+{
+ // constants, properties, methods
+
+}
+
+class ClassName extends ParentClass implements
+ \ArrayAccess,
+ \Foo\Bar\Countable,
+ \Serializable {
+ // constants, properties, methods
+
+}
+
+class ClassName extends ParentClass implements \ArrayAccess,
+ \Countable,
+ \Serializable
+{
+ // constants, properties, methods
+}
+
+class ClassName extends ParentClass implements
+ \ArrayAccess, \Countable, \Foo\Serializable
+{
+ // constants, properties, methods
+}
+
+// Different indent
+if ($foo) {
+ class ClassName extends ParentClass implements
+ \ArrayAccess,
+ \Countable,
+ \Serializable
+ {
+ // constants, properties, methods
+ }
+}
+
+class Foo extends \Foo\Bar\Object
+{
+}
+
+class ClassName extends ParentClass implements
+ \Foo\Bar\Countable,
+ \Serializable
+{
+ // constants, properties, methods
+}
+
+class ClassName extends ParentClass implements
+ \Foo\Bar\Countable ,
+ \Serializable
+{
+ // constants, properties, methods
+}
+
+class Test
+{
+ public function test() {
+ if (1) 1;
+ 1 ? (1 ? 1 : 1) : 1;
+ }
+}
+
+class MyClass
+{
+}
+
+class MyClass
+{
+
+}
+
+class MyClass
+{
+ // Foo.
+}
+
+class MyClass
+{
+ // Foo.
+
+}
+
+abstract class Test implements
+ TestInterface1,
+ TestInterface2
+{
+}
+
+interface MyInterface extends LongInterfaceName1, LongInterfaceName2, LongInterfaceName3, LoginInterfaceName4
+{
+}
+
+interface MyInterface extends
+ LongInterfaceName1,
+ LongInterfaceName2,
+ LongInterfaceName3,
+ LoginInterfaceName4
+{
+}
+
+interface MyInterface extends
+ LongInterfaceName1,
+ LongInterfaceName2,
+ LongInterfaceName3,
+LongInterfaceName4
+{
+}
+
+abstract
+class Test
+{
+}
+
+class ClassName implements
+
+ \ArrayAccess,\Countable,
+\Serializable
+{
+ // constants, properties, methods
+}
+
+class C1
+{
+
+} // C1
+
+class Base
+{
+ protected $anonymous;
+
+ public function __construct()
+ {
+ $this->anonymous = new class extends ArrayObject
+ {
+ public function __construct()
+ {
+ parent::__construct(['a' => 1, 'b' => 2]);
+ }
+ };
+ }
+}
+
+class A extends B
+ implements C
+{
+}
--- /dev/null
+<?php
+class ClassName extends ParentClass implements \ArrayAccess, \Countable
+{
+ // constants, properties, methods
+}
+
+class ClassName extends ParentClass, AnotherParentClass implements \ArrayAccess, \Countable
+{
+ // constants, properties, methods
+}
+
+class ClassName extends ParentClass implements \ArrayAccess, \Countable
+{
+ // constants, properties, methods
+}
+
+class ClassName extends ParentClass implements
+ \ArrayAccess,
+ \Foo\Bar\Countable,
+ \Serializable
+{
+ // constants, properties, methods
+}
+
+class ClassName extends ParentClass implements
+ \ArrayAccess,
+ \Countable,
+ \Serializable
+{
+ // constants, properties, methods
+}
+
+class ClassName extends ParentClass implements
+ \ArrayAccess,
+ \Countable,
+ \Foo\Serializable
+{
+ // constants, properties, methods
+}
+
+// Different indent
+if ($foo) {
+ class ClassName extends ParentClass implements
+ \ArrayAccess,
+ \Countable,
+ \Serializable
+ {
+ // constants, properties, methods
+ }
+}
+
+class Foo extends \Foo\Bar\Object
+{
+}
+
+class ClassName extends ParentClass implements
+ \Foo\Bar\Countable,
+ \Serializable
+{
+ // constants, properties, methods
+}
+
+class ClassName extends ParentClass implements
+ \Foo\Bar\Countable,
+ \Serializable
+{
+ // constants, properties, methods
+}
+
+class Test
+{
+ public function test() {
+ if (1) 1;
+ 1 ? (1 ? 1 : 1) : 1;
+ }
+}
+
+class MyClass
+{
+}
+
+class MyClass
+{
+
+}
+
+class MyClass
+{
+ // Foo.
+}
+
+class MyClass
+{
+ // Foo.
+}
+
+abstract class Test implements
+ TestInterface1,
+ TestInterface2
+{
+}
+
+interface MyInterface extends LongInterfaceName1, LongInterfaceName2, LongInterfaceName3, LoginInterfaceName4
+{
+}
+
+interface MyInterface extends
+ LongInterfaceName1,
+ LongInterfaceName2,
+ LongInterfaceName3,
+ LoginInterfaceName4
+{
+}
+
+interface MyInterface extends
+ LongInterfaceName1,
+ LongInterfaceName2,
+ LongInterfaceName3,
+ LongInterfaceName4
+{
+}
+
+abstract class Test
+{
+}
+
+class ClassName implements
+ \ArrayAccess,
+ \Countable,
+ \Serializable
+{
+ // constants, properties, methods
+}
+
+class C1
+{
+
+} // C1
+
+class Base
+{
+ protected $anonymous;
+
+ public function __construct()
+ {
+ $this->anonymous = new class extends ArrayObject
+ {
+ public function __construct()
+ {
+ parent::__construct(['a' => 1, 'b' => 2]);
+ }
+ };
+ }
+}
+
+class A extends B implements C
+{
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClassDeclaration 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\Standards\PSR2\Tests\Classes;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClassDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 7 => 3,
+ 12 => 1,
+ 13 => 1,
+ 17 => 1,
+ 19 => 2,
+ 20 => 1,
+ 21 => 1,
+ 22 => 1,
+ 25 => 1,
+ 27 => 2,
+ 34 => 1,
+ 35 => 2,
+ 44 => 1,
+ 45 => 1,
+ 63 => 1,
+ 95 => 1,
+ 116 => 1,
+ 118 => 1,
+ 119 => 1,
+ 124 => 1,
+ 130 => 2,
+ 131 => 1,
+ 158 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class MyClass
+{
+ public $var = null;
+ protected $var = null;
+ private $var = null;
+ $var = null;
+
+ var $var = null;
+ public var $var = null;
+
+ public $_var = null;
+ protected $_var = null;
+ private $_var = null;
+
+ public $foo, $bar, $var = null;
+ public $foo,
+ $bar,
+ $var = null;
+}
+
+class foo
+{
+ const bar = <<<BAZ
+qux
+BAZ;
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the PropertyDeclaration 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\Standards\PSR2\Tests\Classes;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class PropertyDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 7 => 1,
+ 9 => 2,
+ 10 => 1,
+ 16 => 1,
+ 17 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 12 => 1,
+ 13 => 1,
+ 14 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+if ($something) {
+}
+foreach ($this as $that ) {
+}
+while (true) {
+ for ($i = 0; $i < 10; $i++) {
+ }
+ if ($something) {
+ }
+
+ foreach ($this as $that) {
+ do {
+ } while ( true );
+
+ }
+}
+
+if ($defaultPageDesign === 0
+ && $defaultCascade === TRUE
+ && $defaultChildDesign === 0
+) {
+ $settingsUpdated = FALSE;
+}
+
+foreach ( $blah as $var ) {
+ if ( $blah ) {
+ }
+}
+
+if (
+ $defaultPageDesign === 0
+ && $defaultCascade === TRUE
+ && $defaultChildDesign === 0
+) {
+ $settingsUpdated = FALSE;
+}
+
+// @codingStandardsChangeSetting PSR2.ControlStructures.ControlStructureSpacing requiredSpacesAfterOpen 1
+// @codingStandardsChangeSetting PSR2.ControlStructures.ControlStructureSpacing requiredSpacesBeforeClose 1
+foreach ($something as $blah => $that) {}
+foreach ( $something as $blah => $that ) {}
+foreach ( $something as $blah => $that ) {}
+// @codingStandardsChangeSetting PSR2.ControlStructures.ControlStructureSpacing requiredSpacesAfterOpen 0
+// @codingStandardsChangeSetting PSR2.ControlStructures.ControlStructureSpacing requiredSpacesBeforeClose 0
+
+$binary = b"binary string";
--- /dev/null
+<?php
+if ($something) {
+}
+foreach ($this as $that) {
+}
+while (true) {
+ for ($i = 0; $i < 10; $i++) {
+ }
+ if ($something) {
+ }
+
+ foreach ($this as $that) {
+ do {
+ } while (true);
+
+ }
+}
+
+if ($defaultPageDesign === 0
+ && $defaultCascade === TRUE
+ && $defaultChildDesign === 0
+) {
+ $settingsUpdated = FALSE;
+}
+
+foreach ($blah as $var) {
+ if ($blah) {
+ }
+}
+
+if ($defaultPageDesign === 0
+ && $defaultCascade === TRUE
+ && $defaultChildDesign === 0
+) {
+ $settingsUpdated = FALSE;
+}
+
+// @codingStandardsChangeSetting PSR2.ControlStructures.ControlStructureSpacing requiredSpacesAfterOpen 1
+// @codingStandardsChangeSetting PSR2.ControlStructures.ControlStructureSpacing requiredSpacesBeforeClose 1
+foreach ( $something as $blah => $that ) {}
+foreach ( $something as $blah => $that ) {}
+foreach ( $something as $blah => $that ) {}
+// @codingStandardsChangeSetting PSR2.ControlStructures.ControlStructureSpacing requiredSpacesAfterOpen 0
+// @codingStandardsChangeSetting PSR2.ControlStructures.ControlStructureSpacing requiredSpacesBeforeClose 0
+
+$binary = b"binary string";
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionSpacing 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\Standards\PSR2\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ControlStructureSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 4 => 1,
+ 14 => 2,
+ 26 => 2,
+ 27 => 2,
+ 31 => 1,
+ 41 => 2,
+ 43 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+if ($something) {
+} else if ($somethingElse) {
+}
+
+if ($something) {
+} elseif ($somethingElse) {
+}
+
+if ($something) {
+} else if ($somethingElse) {
+} elseif ($another) {
+} else {
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+if ($something) {
+} elseif ($somethingElse) {
+}
+
+if ($something) {
+} elseif ($somethingElse) {
+}
+
+if ($something) {
+} elseif ($somethingElse) {
+} elseif ($another) {
+} else {
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ElseIfDeclaration 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\Standards\PSR2\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ElseIfDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 4 => 1,
+ 12 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+switch ($expr) {
+ case 0:
+ echo 'First case, with a break';
+ break;
+ case 1:
+ echo 'Second case, which falls through';
+ // no break
+ case 2:
+ case 3:
+ Case 4:
+ echo 'Third case, return instead of break';
+ return;
+ Default:
+ echo 'Default case';
+ break;
+}
+
+switch ($expr) {
+ case 0:
+ echo 'First case,';
+
+ case 1 :
+ echo 'Second case';
+ // no break
+ case 2:
+ case 3:
+ echo 'Third case';
+ return;
+
+ default:
+ echo 'Default case';
+ break;
+}
+
+switch ($foo) {
+ case'Foo': {
+ echo 'foo';
+ break;
+ }
+}
+
+while ($i < 10) {
+ switch ($foo) {
+ case '1':
+ case '2':
+ ++$i;
+ continue 2;
+ case '3':
+ return $i;
+ }
+}
+
+switch (true) {
+ case is_resource($value):
+ throw new Exception('foo');
+ case is_object($value):
+ return 'object';
+}
+
+switch (0) {
+ case 0:
+ switch (1) {
+ case 1:
+ echo 'a';
+ break;
+ }
+ break;
+}
+
+switch ($foo) {
+ case Foo::ONE:
+ case Foo::TWO:
+ case Foo::Class:
+ break;
+}
+
+switch (true) {
+ case $value instanceof StdClass:
+ return 1;
+ case strpos('_', get_class($value)) !== false:
+ break;
+}
+
+switch (true) {
+ case $value instanceof StdClass:
+ if ($value) {
+ return null;
+ }
+}
+
+use Vendor\Test\FooBar;
+
+function test()
+{
+ switch ($val) {
+ case 'foo':
+ echo 'foo';
+ break;
+ default:
+ echo 'foo';
+ }
+
+ exit;
+}
+
+switch ($foo) {
+ case 1: $bar = 1; break;
+ case 2:
+
+ $bar = 2; break;
+ case 21:
+ case 3: return 3;
+ default: $bar = 0;
+}
+
+switch ($foo) {
+ case 'foo': // some comment
+ echo 'foo';
+ break;
+ case 'bar':
+ // some comment
+ echo 'bar';
+ break;
+ case 'baz': // other comment
+ echo 'baz';
+ break;
+ case 'boo':
+
+ // other comment
+ echo 'boo';
+ break;
+ default: // other comment
+ echo 'default';
+ break;
+}
+
+switch($foo)
+{
+ case ('foo'):
+ default:
+ {
+ $foo = 'foo';
+ break;
+ }
+}
+
+switch ($foo) {
+ case Foo::INTERFACE:
+ return self::INTERFACE;
+ case Foo::NAMESPACE:
+ return self::MODULE;
+ case Foo::TRAIT:
+ case Foo::ARRAY:
+ return self::VALUE;
+}
+
+// OK: Every clause terminates
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ return 0;
+ } else {
+ return 1;
+ }
+ case 2:
+ return 2;
+}
+
+// ERROR: No else clause
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ return 0;
+ } elseif ($bar < 0) {
+ return 1;
+ }
+ case 2:
+ return 2;
+}
+
+// OK: No fall-through present
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ return 0;
+ } elseif ($bar < 0) {
+ return 1;
+ }
+}
+
+// ERROR: No else clause (nested)
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ return 0;
+ } else {
+ if ($foo > $bar) {
+ continue;
+ }
+ }
+ case 2:
+ return 2;
+}
+
+// OK: Every clause terminates
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ return 0;
+ } else {
+ if ($foo > $bar) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ case 2:
+ return 2;
+}
+
+// ERROR: Non-termination IF clause
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ $offset = 0;
+ } else {
+ break;
+ }
+ case 2:
+ return 2;
+}
+
+// ERROR: Non-termination IF clause (nested)
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ continue;
+ } else {
+ if ($foo > $bar) {
+ $offset = 0;
+ } else {
+ break;
+ }
+ }
+ case 2:
+ return 2;
+}
+
+switch ($sContext)
+{
+ case 'SOMETHING':
+ case 'CONSTANT':
+ do_something();
+ break;
+ case 'GLOBAL':
+ case 'GLOBAL1':
+ do_something();
+ // Fall through
+ default:
+ {
+ do_something();
+ }
+}
--- /dev/null
+<?php
+switch ($expr) {
+ case 0:
+ echo 'First case, with a break';
+ break;
+ case 1:
+ echo 'Second case, which falls through';
+ // no break
+ case 2:
+ case 3:
+ case 4:
+ echo 'Third case, return instead of break';
+ return;
+ default:
+ echo 'Default case';
+ break;
+}
+
+switch ($expr) {
+ case 0:
+ echo 'First case,';
+
+ case 1:
+ echo 'Second case';
+ // no break
+ case 2:
+ case 3:
+ echo 'Third case';
+ return;
+
+ default:
+ echo 'Default case';
+ break;
+}
+
+switch ($foo) {
+ case 'Foo': {
+ echo 'foo';
+ break;
+ }
+}
+
+while ($i < 10) {
+ switch ($foo) {
+ case '1':
+ case '2':
+ ++$i;
+ continue 2;
+ case '3':
+ return $i;
+ }
+}
+
+switch (true) {
+ case is_resource($value):
+ throw new Exception('foo');
+ case is_object($value):
+ return 'object';
+}
+
+switch (0) {
+ case 0:
+ switch (1) {
+ case 1:
+ echo 'a';
+ break;
+ }
+ break;
+}
+
+switch ($foo) {
+ case Foo::ONE:
+ case Foo::TWO:
+ case Foo::Class:
+ break;
+}
+
+switch (true) {
+ case $value instanceof StdClass:
+ return 1;
+ case strpos('_', get_class($value)) !== false:
+ break;
+}
+
+switch (true) {
+ case $value instanceof StdClass:
+ if ($value) {
+ return null;
+ }
+}
+
+use Vendor\Test\FooBar;
+
+function test()
+{
+ switch ($val) {
+ case 'foo':
+ echo 'foo';
+ break;
+ default:
+ echo 'foo';
+ }
+
+ exit;
+}
+
+switch ($foo) {
+ case 1:
+ $bar = 1;
+ break;
+ case 2:
+ $bar = 2;
+ break;
+ case 21:
+ case 3:
+ return 3;
+ default:
+ $bar = 0;
+}
+
+switch ($foo) {
+ case 'foo': // some comment
+ echo 'foo';
+ break;
+ case 'bar':
+ // some comment
+ echo 'bar';
+ break;
+ case 'baz': // other comment
+ echo 'baz';
+ break;
+ case 'boo':
+ // other comment
+ echo 'boo';
+ break;
+ default: // other comment
+ echo 'default';
+ break;
+}
+
+switch($foo)
+{
+ case ('foo'):
+ default:
+ {
+ $foo = 'foo';
+ break;
+ }
+}
+
+switch ($foo) {
+ case Foo::INTERFACE:
+ return self::INTERFACE;
+ case Foo::NAMESPACE:
+ return self::MODULE;
+ case Foo::TRAIT:
+ case Foo::ARRAY:
+ return self::VALUE;
+}
+
+// OK: Every clause terminates
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ return 0;
+ } else {
+ return 1;
+ }
+ case 2:
+ return 2;
+}
+
+// ERROR: No else clause
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ return 0;
+ } elseif ($bar < 0) {
+ return 1;
+ }
+ case 2:
+ return 2;
+}
+
+// OK: No fall-through present
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ return 0;
+ } elseif ($bar < 0) {
+ return 1;
+ }
+}
+
+// ERROR: No else clause (nested)
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ return 0;
+ } else {
+ if ($foo > $bar) {
+ continue;
+ }
+ }
+ case 2:
+ return 2;
+}
+
+// OK: Every clause terminates
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ return 0;
+ } else {
+ if ($foo > $bar) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ case 2:
+ return 2;
+}
+
+// ERROR: Non-termination IF clause
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ $offset = 0;
+ } else {
+ break;
+ }
+ case 2:
+ return 2;
+}
+
+// ERROR: Non-termination IF clause (nested)
+switch ($foo) {
+ case 1:
+ if ($bar > 0) {
+ continue;
+ } else {
+ if ($foo > $bar) {
+ $offset = 0;
+ } else {
+ break;
+ }
+ }
+ case 2:
+ return 2;
+}
+
+switch ($sContext)
+{
+ case 'SOMETHING':
+ case 'CONSTANT':
+ do_something();
+ break;
+ case 'GLOBAL':
+ case 'GLOBAL1':
+ do_something();
+ // Fall through
+ default:
+ {
+ do_something();
+ }
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the SwitchDeclaration 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\Standards\PSR2\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class SwitchDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 10 => 1,
+ 11 => 1,
+ 14 => 1,
+ 16 => 1,
+ 20 => 1,
+ 23 => 1,
+ 29 => 1,
+ 33 => 1,
+ 37 => 2,
+ 108 => 2,
+ 109 => 1,
+ 111 => 1,
+ 113 => 2,
+ 114 => 1,
+ 128 => 1,
+ 141 => 1,
+ 172 => 1,
+ 194 => 1,
+ 224 => 1,
+ 236 => 1,
+ 260 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+echo 'hi';
+
+?>
+
+<?php
+
+echo 'bye';
+
+?>
+
--- /dev/null
+<?php
+
+echo 'hi';
+
+?>
+
+<?php
+
+echo 'bye';
+
+
+
--- /dev/null
+<div class="clear"></div>
+ <?php include('inc.php'); ?>
+</div>
\ No newline at end of file
--- /dev/null
+<?php
+$arr = [1, 2, 3];
+?>
+
+A: <?= $arr[0] ?>
+B: <?= $arr[1] ?>
+C: <?= $arr[2] ?>
\ No newline at end of file
--- /dev/null
+<?php echo $foo //hello ?>
--- /dev/null
+<?php echo $foo; //hello
--- /dev/null
+<?php $foo = true; if ($foo) { echo 'hi'; } //hello ?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClosingTag 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\Standards\PSR2\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClosingTagUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ switch ($testFile) {
+ case 'ClosingTagUnitTest.1.inc':
+ return array(11 => 1);
+
+ case 'ClosingTagUnitTest.4.inc':
+ case 'ClosingTagUnitTest.5.inc':
+ return array(1 => 1);
+
+ default:
+ return array();
+ }
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+echo 'foo';
+
--- /dev/null
+<?php
+/** Why me?
+
--- /dev/null
+<?php
+echo 'foo';
--- /dev/null
+<?php
+echo 'foo';
\ No newline at end of file
--- /dev/null
+<?php
+echo 'foo';
+?>
+</body>
--- /dev/null
+<?php
+echo 'foo';
+?>
+</body>
+<?php
+echo 'bar';
--- /dev/null
+<?php
+echo 'foo';
\ No newline at end of file
--- /dev/null
+<?php
+echo 'foo';
+
+
+
+
+
+
+
+
+
--- /dev/null
+<?php
+/** Why me?
--- /dev/null
+<?php
+/** Why me?
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the EndFileNewline 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\Standards\PSR2\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class EndFileNewlineUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ switch ($testFile) {
+ case 'EndFileNewlineUnitTest.1.inc':
+ case 'EndFileNewlineUnitTest.3.inc':
+ case 'EndFileNewlineUnitTest.6.inc':
+ case 'EndFileNewlineUnitTest.7.inc':
+ return array(2 => 1);
+ case 'EndFileNewlineUnitTest.9.inc':
+ case 'EndFileNewlineUnitTest.10.inc':
+ // HHVM just removes the entire comment token, as if it was never there.
+ if (defined('HHVM_VERSION') === true) {
+ return array();
+ }
+ return array(2 => 1);
+ default:
+ return array();
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='')
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// ok
+somefunction1($foo, $bar, [
+ // ...
+], $baz);
+
+// ok
+$app->get('/hello/{name}', function ($name) use ($app) {
+ return 'Hello '.$app->escape($name);
+}, array(
+ '1',
+ '2',
+ '3',
+));
+
+// error
+somefunction2($foo, $bar, [
+ // ...
+ ],
+$baz);
+
+// ok
+somefunction3(// ...
+ $foo,
+ $bar,
+ [
+ // ...
+ ],
+ $baz
+);
+
+// ok
+somefunction4('
+ this should not
+ give an error
+ because it\'s actually
+ one line call
+ with multi-line string
+');
+
+// ok
+somefunction5("hey,
+multi-line string with some
+extra args", $foo, 12);
+
+// error
+somefunction6('
+ but args in a new line
+ are not ok…
+ ',
+ $foo
+);
+
+$this->setFoo(true
+ ? 1
+ : 2, false, array(
+ 'value',
+ 'more'));
+
+$this->setFoo('some'
+ . 'long'
+ . 'text', 'string');
+
+foo(bar(), $a);
+foo();bar();
+
+foo(
+ true
+);
+
+myFunction(<<<END
+Foo
+END
+);
+
+var_dump(array(<<<'EOD'
+foobar!
+EOD
+));
+
+myFunction(<<<END
+Foo
+END
+, 'bar');
+
+myFunction(<<<END
+Foo
+END
+,
+'bar');
+
+if (array_filter(
+ $commands,
+ function ($cmd) use ($commandName) {
+ return ($cmd['name'] == $commandName);
+ }
+)) {
+ // Do something
+}
+
+myFunction(
+ 'foo', (object) array(
+ 'bar' => function ($x) {
+ return true;
+ },
+ 'baz' => false
+ )
+);
+$qux = array_filter(
+ $quux, function ($x) {
+ return $x;
+ }
+);
+
+$this->listeners[] = $events->getSharedManager()->attach(
+ 'Zend\Mvc\Application', MvcEvent::EVENT_DISPATCH, [$this, 'selectLayout'], 100
+);
+
+// @codingStandardsChangeSetting PSR2.Methods.FunctionCallSignature requiredSpacesBeforeClose 1
+foo('Testing
+ multiline text'
+ );
+
+foo('Testing
+ multiline text: ' // . $text
+ );
+
+foo('Testing
+ multiline text: ' /* . $text */
+ );
+
+foo('Testing
+ multiline text: ' /* . $text */
+ // . $other_text
+ );
+
+foo('Testing
+ multiline text: ' /*
+ . $text
+// . $text2
+ */
+ );
+// @codingStandardsChangeSetting PSR2.Methods.FunctionCallSignature requiredSpacesBeforeClose 0
+
+foo('Testing
+ multiline text'
+);
+
+foo('Testing
+ multiline text'
+ );
+
+foo('Testing
+ multiline text' // hello
+);
+
+foo('Testing
+ multiline text' /* hello */
+);
+
+foo('Testing
+ multiline text'
+ // hello
+);
+
+foo('Testing
+ multiline text'
+ /* hello */
+);
+
+$var = foo('Testing
+ multiline'
+ // hi
+ ) + foo('Testing
+ multiline'
+ // hi
+ )
+;
+
+class Test
+{
+ public function getInstance()
+ {
+ return new static(
+ 'arg',
+ 'foo'
+ );
+ }
+
+ public function getSelf()
+ {
+ return new self(
+ 'a', 'b', 'c'
+ );
+ }
+}
+
+$x = $var('y',
+ 'x');
+
+$obj->{$x}(1,
+2);
+
+(function ($a, $b) {
+ return function ($c, $d) use ($a, $b) {
+ echo $a, $b, $c, $d;
+ };
+})(
+ 'a','b'
+)('c',
+ 'd');
--- /dev/null
+<?php
+
+// ok
+somefunction1($foo, $bar, [
+ // ...
+], $baz);
+
+// ok
+$app->get('/hello/{name}', function ($name) use ($app) {
+ return 'Hello '.$app->escape($name);
+}, array(
+ '1',
+ '2',
+ '3',
+));
+
+// error
+somefunction2(
+ $foo,
+ $bar,
+ [
+ // ...
+ ],
+ $baz
+);
+
+// ok
+somefunction3(// ...
+ $foo,
+ $bar,
+ [
+ // ...
+ ],
+ $baz
+);
+
+// ok
+somefunction4('
+ this should not
+ give an error
+ because it\'s actually
+ one line call
+ with multi-line string
+');
+
+// ok
+somefunction5("hey,
+multi-line string with some
+extra args", $foo, 12);
+
+// error
+somefunction6(
+ '
+ but args in a new line
+ are not ok…
+ ',
+ $foo
+);
+
+$this->setFoo(true
+ ? 1
+ : 2, false, array(
+ 'value',
+ 'more'));
+
+$this->setFoo('some'
+ . 'long'
+ . 'text', 'string');
+
+foo(bar(), $a);
+foo();bar();
+
+foo(
+ true
+);
+
+myFunction(<<<END
+Foo
+END
+);
+
+var_dump(array(<<<'EOD'
+foobar!
+EOD
+));
+
+myFunction(<<<END
+Foo
+END
+, 'bar');
+
+myFunction(
+ <<<END
+Foo
+END
+ ,
+ 'bar'
+);
+
+if (array_filter(
+ $commands,
+ function ($cmd) use ($commandName) {
+ return ($cmd['name'] == $commandName);
+ }
+)) {
+ // Do something
+}
+
+myFunction(
+ 'foo',
+ (object) array(
+ 'bar' => function ($x) {
+ return true;
+ },
+ 'baz' => false
+ )
+);
+$qux = array_filter(
+ $quux,
+ function ($x) {
+ return $x;
+ }
+);
+
+$this->listeners[] = $events->getSharedManager()->attach(
+ 'Zend\Mvc\Application',
+ MvcEvent::EVENT_DISPATCH,
+ [$this, 'selectLayout'],
+ 100
+);
+
+// @codingStandardsChangeSetting PSR2.Methods.FunctionCallSignature requiredSpacesBeforeClose 1
+foo('Testing
+ multiline text' );
+
+foo('Testing
+ multiline text: ' ); // . $text
+
+
+foo('Testing
+ multiline text: ' /* . $text */ );
+
+foo('Testing
+ multiline text: ' /* . $text */ );
+ // . $other_text
+
+
+foo('Testing
+ multiline text: ' /*
+ . $text
+// . $text2
+ */ );
+// @codingStandardsChangeSetting PSR2.Methods.FunctionCallSignature requiredSpacesBeforeClose 0
+
+foo('Testing
+ multiline text');
+
+foo('Testing
+ multiline text');
+
+foo('Testing
+ multiline text'); // hello
+
+
+foo('Testing
+ multiline text' /* hello */);
+
+foo('Testing
+ multiline text');
+ // hello
+
+
+foo('Testing
+ multiline text'
+ /* hello */);
+
+$var = foo('Testing
+ multiline')
+ // hi
+ + foo('Testing
+ multiline');
+ // hi
+
+
+class Test
+{
+ public function getInstance()
+ {
+ return new static(
+ 'arg',
+ 'foo'
+ );
+ }
+
+ public function getSelf()
+ {
+ return new self(
+ 'a',
+ 'b',
+ 'c'
+ );
+ }
+}
+
+$x = $var(
+ 'y',
+ 'x'
+);
+
+$obj->{$x}(
+ 1,
+ 2
+);
+
+(function ($a, $b) {
+ return function ($c, $d) use ($a, $b) {
+ echo $a, $b, $c, $d;
+ };
+})(
+ 'a',
+ 'b'
+)(
+ 'c',
+ 'd'
+);
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionCallSignature 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\Standards\PSR2\Tests\Methods;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionCallSignatureUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 18 => 3,
+ 21 => 1,
+ 48 => 1,
+ 87 => 1,
+ 90 => 1,
+ 91 => 1,
+ 103 => 1,
+ 111 => 1,
+ 117 => 4,
+ 121 => 1,
+ 125 => 1,
+ 129 => 1,
+ 133 => 1,
+ 138 => 1,
+ 146 => 1,
+ 150 => 1,
+ 154 => 1,
+ 158 => 1,
+ 162 => 1,
+ 167 => 1,
+ 172 => 1,
+ 175 => 1,
+ 178 => 1,
+ 186 => 1,
+ 187 => 1,
+ 194 => 3,
+ 199 => 1,
+ 200 => 2,
+ 202 => 1,
+ 203 => 1,
+ 210 => 2,
+ 211 => 1,
+ 212 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+function test()
+{
+ // Body here.
+}
+
+function test()
+{
+ echo 'foo';}
+
+function test()
+{
+ // Body here.
+
+}
+
+function test()
+{
+ // Body here.
+
+
+}
+
+class MyClass
+{
+ function test()
+ {
+ // Body here.
+ }
+
+ function test()
+ {
+ echo 'foo';}
+
+ function test()
+ {
+ // Body here.
+
+ }
+
+ function test()
+ {
+ // Body here.
+
+
+ }
+}
+
+$foo = function test()
+{
+ // Body here.
+};
+
+$foo = function test()
+{
+ echo 'foo';};
+
+$foo = function test()
+{
+ // Body here.
+
+};
+
+$foo = function test()
+{
+ // Body here.
+
+
+};
--- /dev/null
+<?php
+
+function test()
+{
+ // Body here.
+}
+
+function test()
+{
+ echo 'foo';}
+
+function test()
+{
+ // Body here.
+}
+
+function test()
+{
+ // Body here.
+}
+
+class MyClass
+{
+ function test()
+ {
+ // Body here.
+ }
+
+ function test()
+ {
+ echo 'foo';}
+
+ function test()
+ {
+ // Body here.
+ }
+
+ function test()
+ {
+ // Body here.
+ }
+}
+
+$foo = function test()
+{
+ // Body here.
+};
+
+$foo = function test()
+{
+ echo 'foo';};
+
+$foo = function test()
+{
+ // Body here.
+};
+
+$foo = function test()
+{
+ // Body here.
+};
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionClosingBrace 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\Standards\PSR2\Tests\Methods;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionClosingBraceUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 16 => 1,
+ 23 => 1,
+ 40 => 1,
+ 47 => 1,
+ 63 => 1,
+ 70 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+class MyClass
+{
+ function _myFunction() {}
+ private function myFunction() {}
+ function __myFunction() {}
+ public static function myFunction() {}
+ static public function myFunction() {}
+ final public function myFunction() {}
+ public final function myFunction() {}
+ abstract private function myFunction();
+ private abstract function myFunction();
+ final public static function myFunction() {}
+ static protected final abstract function myFunction();
+ public function _() {}
+}
--- /dev/null
+<?php
+
+class MyClass
+{
+ function _myFunction() {}
+ private function myFunction() {}
+ function __myFunction() {}
+ public static function myFunction() {}
+ public static function myFunction() {}
+ final public function myFunction() {}
+ final public function myFunction() {}
+ abstract private function myFunction();
+ abstract private function myFunction();
+ final public static function myFunction() {}
+ abstract final protected static function myFunction();
+ public function _() {}
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the MethodDeclaration 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\Standards\PSR2\Tests\Methods;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class MethodDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 9 => 1,
+ 11 => 1,
+ 13 => 1,
+ 15 => 3,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(5 => 1);
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+namespace Vendor\Package;
+
+use FooClass;
+
+namespace Vendor\Package;
+use BarClass as Bar;
+
+namespace Vendor\Package;
+
+
+ use BarClass as Bar;
+
+namespace Vendor\Package;
--- /dev/null
+<?php
+namespace Vendor\Package;
+
+use FooClass;
+
+namespace Vendor\Package;
+
+use BarClass as Bar;
+
+namespace Vendor\Package;
+
+ use BarClass as Bar;
+
+namespace Vendor\Package;
--- /dev/null
+<?php
+/**
+ * Unit test class for the NamespaceDeclaration 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\Standards\PSR2\Tests\Namespaces;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class NamespaceDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 6 => 1,
+ 9 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+namespace MyProject;
+
+use BarClass as Bar;
+use My\Full\Classname as Another;
+
+
+use Something;
+use SomethingElse;
+
+// Comment here.
+use LastThing;
+
+class Foo {
+}
+
+$var = new MyClass(
+ function () use ($foo, $bar) {
+ return true;
+ }
+);
+
+class Container extends Component implements IContainer
+{
+ use TContainer;
+}
+
+trait HelloWorld
+{
+ use Hello, World;
+}
--- /dev/null
+<?php
+namespace MyProject;
+
+use BarClass as Bar;
+use My\Full\Classname as Another, My\Full\NSname;
+
+
+namespace AnotherProject;
+
+use ArrayObject;
+
+
+$foo = 'bar';
+
+?>
--- /dev/null
+<?php
+namespace MyProject;
+
+use BarClass as Bar;
+use My\Full\Classname as Another;
+use My\Full\NSname;
+
+
+namespace AnotherProject;
+
+use ArrayObject;
+
+$foo = 'bar';
+
+?>
--- /dev/null
+<?php
+namespace bug;
+
+use
+ someNS\A;
+use someNS\B;
+class Bug
+{
+ public function __construct()
+ {
+ $b = 1;
+ $a = function () use ($b) {
+ echo $b;
+ };
+ }
+}
--- /dev/null
+<?php
+namespace bug;
+
+use someNS\A;
+use someNS\B;
+
+class Bug
+{
+ public function __construct()
+ {
+ $b = 1;
+ $a = function () use ($b) {
+ echo $b;
+ };
+ }
+}
--- /dev/null
+<?php
+use My\Full\Classname as Another;
+?>
+<h1><?php echo $foo; ?></h1>
--- /dev/null
+<?php
+namespace MyProject;
+
+use Single\ClassA as A;
+use Grouped\Classes\{ClassB, ClassC as C};
+use const Grouped\Constants\{D, E};
+use Single\ClassF;
+use Grouped\Mixed\ {
+ ClassG,
+ const ConstH,
+ function func_i,
+ ClassJ as J
+};
+use function Grouped\Functions\ { func_k };
+
+// Some examples from the original RFC.
+use function foo\math\{ sin, cos, cosh };
+use const foo\math\{ PI, E, GAMMA, GOLDEN_RATIO };
+use foo\math\{ Math, const PI, function sin, function cos, function cosh };
+
+class PHP7 {
+
+}
--- /dev/null
+<?php
+namespace MyProject;
+
+use Single\ClassA as A;
+use Grouped\Classes\ClassB;
+use Grouped\Classes\ClassC as C;
+use const Grouped\Constants\D;
+use const Grouped\Constants\E;
+use Single\ClassF;
+use Grouped\Mixed\ClassG;
+use const Grouped\Mixed\ConstH;
+use function Grouped\Mixed\func_i;
+use Grouped\Mixed\ClassJ as J;
+use function Grouped\Functions\func_k;
+
+// Some examples from the original RFC.
+use function foo\math\sin;
+use function foo\math\cos;
+use function foo\math\cosh;
+use const foo\math\PI;
+use const foo\math\E;
+use const foo\math\GAMMA;
+use const foo\math\GOLDEN_RATIO;
+use foo\math\Math;
+use const foo\math\PI;
+use function foo\math\sin;
+use function foo\math\cos;
+use function foo\math\cosh;
+
+class PHP7 {
+
+}
--- /dev/null
+<?php use My\Full\Classname as Another ?>
--- /dev/null
+<?php use My\Full\Classname as Another
--- /dev/null
+<?php
+/**
+ * Unit test class for the UseDeclaration 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\Standards\PSR2\Tests\Namespaces;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class UseDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ switch ($testFile) {
+ case 'UseDeclarationUnitTest.2.inc':
+ return array(
+ 4 => 1,
+ 5 => 1,
+ 10 => 2,
+ );
+ case 'UseDeclarationUnitTest.3.inc':
+ return array(
+ 4 => 1,
+ 6 => 1,
+ );
+ case 'UseDeclarationUnitTest.5.inc':
+ return array(
+ 5 => 1,
+ 6 => 1,
+ 8 => 1,
+ 14 => 1,
+ 17 => 1,
+ 18 => 1,
+ 19 => 1,
+ );
+ default:
+ return array();
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?xml version="1.0"?>
+<ruleset name="PSR2">
+ <description>The PSR-2 coding standard.</description>
+ <arg name="tab-width" value="4"/>
+
+ <!-- 2. General -->
+
+ <!-- 2.1 Basic Coding Standard -->
+
+ <!-- Include the whole PSR-1 standard -->
+ <rule ref="PSR1"/>
+
+ <!-- 2.2 Files -->
+
+ <!-- All PHP files MUST use the Unix LF (linefeed) line ending. -->
+ <rule ref="Generic.Files.LineEndings">
+ <properties>
+ <property name="eolChar" value="\n"/>
+ </properties>
+ </rule>
+
+ <!-- All PHP files MUST end with a single blank line. -->
+ <!-- checked in Files/EndFileNewlineSniff -->
+
+ <!-- The closing ?> tag MUST be omitted from files containing only PHP. -->
+ <!-- checked in Files/ClosingTagSniff -->
+
+ <!-- 2.3 Lines -->
+
+ <!-- The soft limit on line length MUST be 120 characters; automated style checkers MUST warn but MUST NOT error at the soft limit. -->
+ <rule ref="Generic.Files.LineLength">
+ <properties>
+ <property name="lineLimit" value="120"/>
+ <property name="absoluteLineLimit" value="0"/>
+ </properties>
+ </rule>
+
+ <!-- There MUST NOT be trailing whitespace at the end of non-blank lines. -->
+ <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
+ <properties>
+ <property name="ignoreBlankLines" value="true"/>
+ </properties>
+ </rule>
+ <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.StartFile">
+ <severity>0</severity>
+ </rule>
+ <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.EndFile">
+ <severity>0</severity>
+ </rule>
+ <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.EmptyLines">
+ <severity>0</severity>
+ </rule>
+
+ <!-- There MUST NOT be more than one statement per line. -->
+ <rule ref="Generic.Formatting.DisallowMultipleStatements"/>
+
+ <!-- 2.4 Indenting -->
+
+ <!-- Code MUST use an indent of 4 spaces, and MUST NOT use tabs for indenting. -->
+ <rule ref="Generic.WhiteSpace.ScopeIndent">
+ <properties>
+ <property name="ignoreIndentationTokens" type="array" value="T_COMMENT,T_DOC_COMMENT_OPEN_TAG"/>
+ </properties>
+ </rule>
+ <rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
+
+ <!-- 2.5 Keywords and True/False/Null -->
+
+ <!-- PHP keywords MUST be in lower case. -->
+ <rule ref="Generic.PHP.LowerCaseKeyword"/>
+
+ <!-- The PHP constants true, false, and null MUST be in lower case. -->
+ <rule ref="Generic.PHP.LowerCaseConstant"/>
+
+ <!-- 3. Namespace and Use Declarations -->
+
+ <!-- When present, there MUST be one blank line after the namespace declaration. -->
+ <!-- checked in Namespaces/NamespaceDeclarationSniff -->
+
+ <!-- When present, all use declarations MUST go after the namespace declaration.
+ There MUST be one use keyword per declaration.
+ There MUST be one blank line after the use block. -->
+ <!-- checked in Namespaces/UseDeclarationSniff -->
+
+ <!-- 4. Classes, Properties, and Methods -->
+
+ <!-- 4.1. Extends and Implements -->
+
+ <!-- The extends and implements keywords MUST be declared on the same line as the class name.
+ The opening brace for the class go MUST go on its own line; the closing brace for the class MUST go on the next line after the body.
+ Lists of implements MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one interface per line. -->
+ <!-- checked in Classes/ClassDeclarationSniff -->
+
+ <!-- 4.2. Properties -->
+
+ <!-- Visibility MUST be declared on all properties.
+ The var keyword MUST NOT be used to declare a property.
+ There MUST NOT be more than one property declared per statement.
+ Property names SHOULD NOT be prefixed with a single underscore to indicate protected or private visibility. -->
+ <!-- checked in Classes/PropertyDeclarationSniff -->
+
+ <!-- 4.3 Methods -->
+
+ <!-- Visibility MUST be declared on all methods. -->
+ <rule ref="Squiz.Scope.MethodScope"/>
+ <rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/>
+
+ <!-- Method names SHOULD NOT be prefixed with a single underscore to indicate protected or private visibility. -->
+ <!-- checked in Methods/MethodDeclarationSniff -->
+
+ <!-- Method names MUST NOT be declared with a space after the method name. The opening brace MUST go on its own line, and the closing brace MUST go on the next line following the body. There MUST NOT be a space after the opening parenthesis, and there MUST NOT be a space before the closing parenthesis. -->
+ <!-- checked in Methods/FunctionClosingBraceSniff -->
+ <rule ref="Squiz.Functions.FunctionDeclaration"/>
+ <rule ref="Squiz.Functions.LowercaseFunctionKeywords"/>
+
+ <!-- 4.4 Method Arguments -->
+
+ <!-- In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma. -->
+ <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing">
+ <properties>
+ <property name="equalsSpacing" value="1"/>
+ </properties>
+ </rule>
+ <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingAfterHint">
+ <severity>0</severity>
+ </rule>
+
+ <!-- Method arguments with default values MUST go at the end of the argument list. -->
+ <rule ref="PEAR.Functions.ValidDefaultValue"/>
+
+ <!-- Argument lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument per line. When the argument list is split across multiple lines, the closing parenthesis and opening brace MUST be placed together on their own line with one space between them. -->
+ <rule ref="Squiz.Functions.MultiLineFunctionDeclaration"/>
+
+ <!-- 4.5 abstract, final, and static -->
+
+ <!-- When present, the abstract and final declarations MUST precede the visibility declaration.
+ When present, the static declaration MUST come after the visibility declaration. -->
+ <!-- checked in Methods/MethodDeclarationSniff -->
+
+ <!-- 4.6 Method and Function Calls -->
+
+ <!-- When making a method or function call, there MUST NOT be a space between the method or function name and the opening parenthesis, there MUST NOT be a space after the opening parenthesis, and there MUST NOT be a space before the closing parenthesis. In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma.
+ Argument lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument per line. -->
+ <rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
+ <rule ref="PSR2.Methods.FunctionCallSignature.SpaceAfterCloseBracket">
+ <severity>0</severity>
+ </rule>
+
+ <!-- 5. Control Structures -->
+
+ <!-- The general style rules for control structures are as follows:
+ There MUST be one space after the control structure keyword
+ There MUST NOT be a space after the opening parenthesis
+ There MUST NOT be a space before the closing parenthesis
+ There MUST be one space between the closing parenthesis and the opening brace
+ The structure body MUST be indented once
+ The closing brace MUST be on the next line after the body -->
+ <rule ref="Squiz.ControlStructures.ControlSignature"/>
+ <rule ref="Squiz.WhiteSpace.ControlStructureSpacing.SpacingAfterOpen" />
+ <rule ref="Squiz.WhiteSpace.ControlStructureSpacing.SpacingBeforeClose" />
+ <rule ref="Squiz.WhiteSpace.ScopeClosingBrace"/>
+ <rule ref="Squiz.ControlStructures.ForEachLoopDeclaration"/>
+ <rule ref="Squiz.ControlStructures.ForLoopDeclaration"/>
+ <rule ref="Squiz.ControlStructures.LowercaseDeclaration"/>
+ <!-- checked in ControlStructures/ControlStructureSpacingSniff -->
+
+ <!-- The body of each structure MUST be enclosed by braces. This standardizes how the structures look, and reduces the likelihood of introducing errors as new lines get added to the body. -->
+ <rule ref="Generic.ControlStructures.InlineControlStructure"/>
+
+ <!-- 5.1. if, elseif, else -->
+
+ <!-- The keyword elseif SHOULD be used instead of else if so that all control keywords look like single words. -->
+ <!-- checked in ControlStructures/ElseIfDeclarationSniff -->
+
+ <!-- 5.2. switch, case -->
+
+ <!-- The case statement MUST be indented once from switch, and the break keyword (or other terminating keyword) MUST be indented at the same level as the case body. There MUST be a comment such as // no break when fall-through is intentional in a non-empty case body. -->
+ <!-- checked in ControlStructures/SwitchDeclarationSniff -->
+
+ <!-- 6. Closures -->
+
+ <!-- Closures MUST be declared with a space after the function keyword, and a space before and after the use keyword.
+ The opening brace MUST go on the same line, and the closing brace MUST go on the next line following the body.
+ There MUST NOT be a space after the opening parenthesis of the argument list or variable list, and there MUST NOT be a space before the closing parenthesis of the argument list or variable list.
+ In the argument list and variable list, there MUST NOT be a space before each comma, and there MUST be one space after each comma.
+ Closure arguments with default values MUST go at the end of the argument list.
+ Argument lists and variable lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument or variable per line.
+ When the ending list (whether or arguments or variables) is split across multiple lines, the closing parenthesis and opening brace MUST be placed together on their own line with one space between them. -->
+ <!-- checked in Squiz.Functions.MultiLineFunctionDeclaration -->
+</ruleset>
--- /dev/null
+<documentation title="Array Bracket Spacing">
+ <standard>
+ <![CDATA[
+ When referencing arrays you should not put whitespace around the opening bracket or before the closing bracket.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: No spaces around the brackets.">
+ <![CDATA[
+$foo<em></em>[<em></em>'bar'<em></em>];
+]]>
+ </code>
+ <code title="Invalid: Spaces around the brackets.">
+ <![CDATA[
+$foo<em> </em>[<em> </em>'bar'<em> </em>];
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Array Declarations">
+ <standard>
+ <![CDATA[
+ This standard covers all array declarations, regardless of the number and type of values contained within the array.
+ ]]>
+ </standard>
+ <standard>
+ <![CDATA[
+ The <em>array</em> keyword must be lowercase.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: array keyword lowercase">
+ <![CDATA[
+$array = <em>a</em>rray('val1', 'val2');
+ ]]>
+ </code>
+ <code title="Invalid: first letter capitalised">
+ <![CDATA[
+$array = <em>A</em>rray('val1', 'val2');
+ ]]>
+ </code>
+ </code_comparison>
+ <standard>
+ <![CDATA[
+ The first array key must begin on the line after the <em>array</em> keyword.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: first key on second line">
+ <![CDATA[
+$array = array(
+ <em>'key1'</em> => 'value1',
+ 'key2' => 'value2',
+ );
+ ]]>
+ </code>
+ <code title="Invalid: first key on same line">
+ <![CDATA[
+$array = array(<em>'key1'</em> => 'value1',
+ 'key2' => 'value2',
+ );
+ ]]>
+ </code>
+ </code_comparison>
+ <standard>
+ <![CDATA[
+ All array keys must be indented to one space after the start of the <em>array</em> keyword. The closing parenthesis must be aligned with the start of the <em>array</em> keyword.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: aligned correctly">
+ <![CDATA[
+$array = array(
+ <em> </em>'key1' => 'value1',
+ <em> </em>'key2' => 'value2',
+ );
+ ]]>
+ </code>
+ <code title="Invalid: keys and parenthesis aligned incorrectly">
+ <![CDATA[
+$array = array(
+ <em>'</em>key1' => 'value1',
+ <em>'</em>key2' => 'value2',
+);
+ ]]>
+ </code>
+ </code_comparison>
+ <standard>
+ <![CDATA[
+ All double arrow symbols must be aligned to one space after the longest array key. Alignment must be achieved using spaces.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: keys and values aligned">
+ <![CDATA[
+$array = array(
+ 'keyTen'<em> </em>=> 'ValueTen',
+ 'keyTwenty'<em> </em>=> 'ValueTwenty',
+ );
+ ]]>
+ </code>
+ <code title="Invalid: alignment incorrect">
+ <![CDATA[
+$array = array(
+ 'keyTen'<em> </em>=> 'ValueTen',
+ 'keyTwenty'<em> </em>=> 'ValueTwenty',
+ );
+ ]]>
+ </code>
+ </code_comparison>
+ <standard>
+ <![CDATA[
+ All array values must be followed by a comma, including the final value.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: comma after each value">
+ <![CDATA[
+$array = array(
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ 'key3' => 'value3'<em>,</em>
+ );
+ ]]>
+ </code>
+ <code title="Invalid: no comma after last value">
+ <![CDATA[
+$array = array(
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ 'key3' => 'value3'<em> </em>
+ );
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Lowercase Class Keywords">
+ <standard>
+ <![CDATA[
+ The php keywords class, interface, trait, extends, implements, abstract, final, var, and const should be lowercase.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Lowercase class keywords.">
+ <![CDATA[
+<em>final</em> <em>class</em> Foo <em>extends</em> Bar
+{
+}
+]]>
+ </code>
+ <code title="Invalid: Initial capitalization of class keywords.">
+ <![CDATA[
+<em>Final</em> <em>Class</em> Foo <em>Extends</em> Bar
+{
+}
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Self Member Reference">
+ <standard>
+ <![CDATA[
+ The self keyword should be used instead of the current class name, should be lowercase, and should not have spaces before or after it.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Lowercase self used.">
+ <![CDATA[
+<em>self</em>::foo();
+]]>
+ </code>
+ <code title="Invalid: Uppercase self used.">
+ <![CDATA[
+<em>SELF</em>::foo();
+]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Correct spacing used.">
+ <![CDATA[
+self<em></em>::<em></em>foo();
+]]>
+ </code>
+ <code title="Invalid: Incorrect spacing used.">
+ <![CDATA[
+self<em> </em>::<em> </em>foo();
+]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Self used as reference.">
+ <![CDATA[
+class Foo
+{
+ public static function bar()
+ {
+ }
+
+ public static function baz()
+ {
+ <em>self</em>::bar();
+ }
+}
+]]>
+ </code>
+ <code title="Invalid: Local class name used as reference.">
+ <![CDATA[
+class <em>Foo</em>
+{
+ public static function bar()
+ {
+ }
+
+ public static function baz()
+ {
+ <em>Foo</em>::bar();
+ }
+}
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Doc Comment Alignment">
+ <standard>
+ <![CDATA[
+ The asterisks in a doc comment should align, and there should be one space between the asterisk and tags.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Asterisks are aligned.">
+ <![CDATA[
+/**
+<em> </em>* @see foo()
+<em> </em>*/
+]]>
+ </code>
+ <code title="Invalid: Asterisks are not aligned.">
+ <![CDATA[
+/**
+<em> </em>* @see foo()
+<em></em>*/
+]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: One space between asterisk and tag.">
+ <![CDATA[
+/**
+ *<em> </em>@see foo()
+ */
+]]>
+ </code>
+ <code title="Invalid: Incorrect spacing used.">
+ <![CDATA[
+/**
+ *<em> </em>@see foo()
+ */
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Doc Comment Throws Tag">
+ <standard>
+ <![CDATA[
+ If a function throws any exceptions, they should be documented in a @throws tag.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: @throws tag used.">
+ <![CDATA[
+/**
+ * <em>@throws Exception all the time</em>
+ * @return void
+ */
+function foo()
+{
+ throw new Exception('Danger!');
+}
+]]>
+ </code>
+ <code title="Invalid: No @throws tag used for throwing function.">
+ <![CDATA[
+/**
+ * @return void
+ */
+function foo()
+{
+ throw new Exception('Danger!');
+}
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Foreach Loop Declarations">
+ <standard>
+ <![CDATA[
+ There should be a space between each element of a foreach loop and the as keyword should be lowercase.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Correct spacing used.">
+ <![CDATA[
+foreach (<em></em>$foo<em> </em>as<em> </em>$bar<em> </em>=><em> </em>$baz<em></em>) {
+ echo $baz;
+}
+]]>
+ </code>
+ <code title="Invalid: Invalid spacing used.">
+ <![CDATA[
+foreach (<em> </em>$foo<em> </em>as<em> </em>$bar<em></em>=><em></em>$baz<em> </em>) {
+ echo $baz;
+}
+]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Lowercase as keyword.">
+ <![CDATA[
+foreach ($foo <em>as</em> $bar => $baz) {
+ echo $baz;
+}
+]]>
+ </code>
+ <code title="Invalid: Uppercase as keyword.">
+ <![CDATA[
+foreach ($foo <em>AS</em> $bar => $baz) {
+ echo $baz;
+}
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="For Loop Declarations">
+ <standard>
+ <![CDATA[
+ In a for loop declaration, there should be no space inside the brackets and there should be 0 spaces before and 1 space after semicolons.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Correct spacing used.">
+ <![CDATA[
+for (<em></em>$i = 0; $i < 10; $i++<em></em>) {
+ echo $i;
+}
+]]>
+ </code>
+ <code title="Invalid: Invalid spacing used inside brackets.">
+ <![CDATA[
+for (<em> </em>$i = 0; $i < 10; $i++<em> </em>) {
+ echo $i;
+}
+]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Correct spacing used.">
+ <![CDATA[
+for ($i = 0<em></em>; $i < 10<em></em>; $i++) {
+ echo $i;
+}
+]]>
+ </code>
+ <code title="Invalid: Invalid spacing used before semicolons.">
+ <![CDATA[
+for ($i = 0<em> </em>; $i < 10<em> </em>; $i++) {
+ echo $i;
+}
+]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: Correct spacing used.">
+ <![CDATA[
+for ($i = 0;<em> </em>$i < 10;<em> </em>$i++) {
+ echo $i;
+}
+]]>
+ </code>
+ <code title="Invalid: Invalid spacing used after semicolons.">
+ <![CDATA[
+for ($i = 0;<em></em>$i < 10;<em></em>$i++) {
+ echo $i;
+}
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Lowercase Control Structure Keywords">
+ <standard>
+ <![CDATA[
+ The php keywords if, else, elseif, foreach, for, do, switch, while, try, and catch should be lowercase.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Lowercase if keyword.">
+ <![CDATA[
+<em>if</em> ($foo) {
+ $bar = true;
+}
+]]>
+ </code>
+ <code title="Invalid: Uppercase if keyword.">
+ <![CDATA[
+<em>IF</em> ($foo) {
+ $bar = true;
+}
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Lowercase Built-In functions">
+ <standard>
+ <![CDATA[
+ All PHP built-in functions should be lowercased when called.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Lowercase function call.">
+ <![CDATA[
+if (<em>isset</em>($foo)) {
+ echo $foo;
+}
+]]>
+ </code>
+ <code title="Invalid: isset not called as lowercase.">
+ <![CDATA[
+if (<em>isSet</em>($foo)) {
+ echo $foo;
+}
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Lowercase Function Keywords">
+ <standard>
+ <![CDATA[
+ The php keywords function, public, private, protected, and static should be lowercase.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Lowercase function keyword.">
+ <![CDATA[
+<em>function</em> foo()
+{
+ return true;
+}
+]]>
+ </code>
+ <code title="Invalid: Uppercase function keyword.">
+ <![CDATA[
+<em>FUNCTION</em> foo()
+{
+ return true;
+}
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Static This Usage">
+ <standard>
+ <![CDATA[
+ Static methods should not use $this.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Using self:: to access static variables.">
+ <![CDATA[
+class Foo
+{
+ <em>static</em> function bar()
+ {
+ return <em>self</em>::$staticMember;
+ }
+}
+]]>
+ </code>
+ <code title="Invalid: Using $this-> to access static variables.">
+ <![CDATA[
+class Foo
+{
+ <em>static</em> function bar()
+ {
+ return <em>$this</em>->$staticMember;
+ }
+}
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Echoed Strings">
+ <standard>
+ <![CDATA[
+ Simple strings should not be enclosed in parentheses when being echoed.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Using echo without parentheses.">
+ <![CDATA[
+echo<em> </em>"Hello";
+]]>
+ </code>
+ <code title="Invalid: Using echo with parentheses.">
+ <![CDATA[
+echo<em>(</em>"Hello"<em>)</em>;
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Cast Whitespace">
+ <standard>
+ <![CDATA[
+ Casts should not have whitespace inside the parentheses.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: No spaces.">
+ <![CDATA[
+$foo = (<em></em>int<em></em>)'42';
+]]>
+ </code>
+ <code title="Invalid: Whitespace used inside parentheses.">
+ <![CDATA[
+$foo = (<em> </em>int<em> </em>)'42';
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Function Opening Brace Whitespace">
+ <standard>
+ <![CDATA[
+ The opening brace for functions should be on a new line with no blank lines surrounding it.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Opening brace is on a new line.">
+ <![CDATA[
+function foo()
+<em>{</em>
+}
+]]>
+ </code>
+ <code title="Invalid: Opening brace is on the same line as the function declaration.">
+ <![CDATA[
+function foo() <em>{</em>
+}
+]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: No blank lines after the opening brace.">
+ <![CDATA[
+function foo()
+{
+<em> return 42;</em>
+}
+]]>
+ </code>
+ <code title="Invalid: A blank line after the opening brace.">
+ <![CDATA[
+function foo()
+{
+<em></em>
+ return 42;
+}
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Language Construct Whitespace">
+ <standard>
+ <![CDATA[
+ The php constructs echo, print, return, include, include_once, require, require_once, and new should have one space after them.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: echo statement with a single space after it.">
+ <![CDATA[
+echo<em> </em>"hi";
+]]>
+ </code>
+ <code title="Invalid: echo statement with no space after it.">
+ <![CDATA[
+echo<em></em>"hi";
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Object Operator Spacing">
+ <standard>
+ <![CDATA[
+ The object operator (->) should not have any space around it.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: No spaces around the object operator.">
+ <![CDATA[
+$foo<em></em>-><em></em>bar();
+]]>
+ </code>
+ <code title="Invalid: Whitespace surrounding the object operator.">
+ <![CDATA[
+$foo<em> </em>-><em> </em>bar();
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Scope Keyword Spacing">
+ <standard>
+ <![CDATA[
+ The php keywords static, public, private, and protected should have one space after them.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A single space following the keywords.">
+ <![CDATA[
+public<em> </em>static<em> </em>function foo()
+{
+}
+]]>
+ </code>
+ <code title="Invalid: Multiple spaces following the keywords.">
+ <![CDATA[
+public<em> </em>static<em> </em>function foo()
+{
+}
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Semicolon Spacing">
+ <standard>
+ <![CDATA[
+ Semicolons should not have spaces before them.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: No space before the semicolon.">
+ <![CDATA[
+echo "hi"<em></em>;
+]]>
+ </code>
+ <code title="Invalid: Space before the semicolon.">
+ <![CDATA[
+echo "hi"<em> </em>;
+]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<?php
+/**
+ * Ensure that there are no spaces around square brackets.
+ *
+ * @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\Standards\Squiz\Sniffs\Arrays;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ArrayBracketSpacingSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_OPEN_SQUARE_BRACKET,
+ T_CLOSE_SQUARE_BRACKET,
+ );
+
+ }//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_OPEN_SQUARE_BRACKET
+ && isset($tokens[$stackPtr]['bracket_closer']) === false)
+ || ($tokens[$stackPtr]['code'] === T_CLOSE_SQUARE_BRACKET
+ && isset($tokens[$stackPtr]['bracket_opener']) === false)
+ ) {
+ // Bow out for parse error/during live coding.
+ return;
+ }
+
+ // Square brackets can not have a space before them.
+ $prevType = $tokens[($stackPtr - 1)]['code'];
+ if ($prevType === T_WHITESPACE) {
+ $nonSpace = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 2), null, true);
+ $expected = $tokens[$nonSpace]['content'].$tokens[$stackPtr]['content'];
+ $found = $phpcsFile->getTokensAsString($nonSpace, ($stackPtr - $nonSpace)).$tokens[$stackPtr]['content'];
+ $error = 'Space found before square bracket; expected "%s" but found "%s"';
+ $data = array(
+ $expected,
+ $found,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceBeforeBracket', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), '');
+ }
+ }
+
+ // Open square brackets can't ever have spaces after them.
+ if ($tokens[$stackPtr]['code'] === T_OPEN_SQUARE_BRACKET) {
+ $nextType = $tokens[($stackPtr + 1)]['code'];
+ if ($nextType === T_WHITESPACE) {
+ $nonSpace = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 2), null, true);
+ $expected = $tokens[$stackPtr]['content'].$tokens[$nonSpace]['content'];
+ $found = $phpcsFile->getTokensAsString($stackPtr, ($nonSpace - $stackPtr + 1));
+ $error = 'Space found after square bracket; expected "%s" but found "%s"';
+ $data = array(
+ $expected,
+ $found,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterBracket', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), '');
+ }
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that arrays conform to the array 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
+ */
+
+namespace PHP_CodeSniffer\Standards\Squiz\Sniffs\Arrays;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ArrayDeclarationSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ 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');
+
+ // Array keyword should be lower case.
+ if ($tokens[$stackPtr]['content'] !== strtolower($tokens[$stackPtr]['content'])) {
+ if ($tokens[$stackPtr]['content'] === strtoupper($tokens[$stackPtr]['content'])) {
+ $phpcsFile->recordMetric($stackPtr, 'Array keyword case', 'upper');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Array keyword case', 'mixed');
+ }
+
+ $error = 'Array keyword should be lower case; expected "array" but found "%s"';
+ $data = array($tokens[$stackPtr]['content']);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotLowerCase', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, 'array');
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Array keyword case', 'lower');
+ }
+
+ $arrayStart = $tokens[$stackPtr]['parenthesis_opener'];
+ if (isset($tokens[$arrayStart]['parenthesis_closer']) === false) {
+ return;
+ }
+
+ $arrayEnd = $tokens[$arrayStart]['parenthesis_closer'];
+
+ if ($arrayStart !== ($stackPtr + 1)) {
+ $error = 'There must be no space between the "array" keyword and the opening parenthesis';
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), $arrayStart, true);
+ if (isset(Tokens::$commentTokens[$tokens[$next]['code']]) === true) {
+ // We don't have anywhere to put the comment, so don't attempt to fix it.
+ $phpcsFile->addError($error, $stackPtr, 'SpaceAfterKeyword');
+ } else {
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterKeyword');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($stackPtr + 1); $i < $arrayStart; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'yes');
+ $arrayStart = $stackPtr;
+ $arrayEnd = $tokens[$stackPtr]['bracket_closer'];
+ }//end if
+
+ // Check for empty arrays.
+ $content = $phpcsFile->findNext(T_WHITESPACE, ($arrayStart + 1), ($arrayEnd + 1), true);
+ if ($content === $arrayEnd) {
+ // Empty array, but if the brackets aren't together, there's a problem.
+ if (($arrayEnd - $arrayStart) !== 1) {
+ $error = 'Empty array declaration must have no space between the parentheses';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceInEmptyArray');
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($arrayStart + 1); $i < $arrayEnd; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ // We can return here because there is nothing else to check. All code
+ // below can assume that the array is not empty.
+ return;
+ }
+
+ if ($tokens[$arrayStart]['line'] === $tokens[$arrayEnd]['line']) {
+ $this->processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd);
+ } else {
+ $this->processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd);
+ }
+
+ }//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.
+ *
+ * @return void
+ */
+ public function processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Check if there are multiple values. If so, then it has to be multiple lines
+ // unless it is contained inside a function call or condition.
+ $valueCount = 0;
+ $commas = array();
+ for ($i = ($arrayStart + 1); $i < $arrayEnd; $i++) {
+ // Skip bracketed statements, like function calls.
+ if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
+ $i = $tokens[$i]['parenthesis_closer'];
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_COMMA) {
+ // Before counting this comma, make sure we are not
+ // at the end of the array.
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), $arrayEnd, true);
+ if ($next !== false) {
+ $valueCount++;
+ $commas[] = $i;
+ } else {
+ // There is a comma at the end of a single line array.
+ $error = 'Comma not allowed after last value in single-line array declaration';
+ $fix = $phpcsFile->addFixableError($error, $i, 'CommaAfterLast');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+ }
+ }
+ }//end for
+
+ // Now check each of the double arrows (if any).
+ $nextArrow = $arrayStart;
+ while (($nextArrow = $phpcsFile->findNext(T_DOUBLE_ARROW, ($nextArrow + 1), $arrayEnd)) !== false) {
+ if ($tokens[($nextArrow - 1)]['code'] !== T_WHITESPACE) {
+ $content = $tokens[($nextArrow - 1)]['content'];
+ $error = 'Expected 1 space between "%s" and double arrow; 0 found';
+ $data = array($content);
+ $fix = $phpcsFile->addFixableError($error, $nextArrow, 'NoSpaceBeforeDoubleArrow', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->addContentBefore($nextArrow, ' ');
+ }
+ } else {
+ $spaceLength = $tokens[($nextArrow - 1)]['length'];
+ if ($spaceLength !== 1) {
+ $content = $tokens[($nextArrow - 2)]['content'];
+ $error = 'Expected 1 space between "%s" and double arrow; %s found';
+ $data = array(
+ $content,
+ $spaceLength,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $nextArrow, 'SpaceBeforeDoubleArrow', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($nextArrow - 1), ' ');
+ }
+ }
+ }//end if
+
+ if ($tokens[($nextArrow + 1)]['code'] !== T_WHITESPACE) {
+ $content = $tokens[($nextArrow + 1)]['content'];
+ $error = 'Expected 1 space between double arrow and "%s"; 0 found';
+ $data = array($content);
+ $fix = $phpcsFile->addFixableError($error, $nextArrow, 'NoSpaceAfterDoubleArrow', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($nextArrow, ' ');
+ }
+ } else {
+ $spaceLength = $tokens[($nextArrow + 1)]['length'];
+ if ($spaceLength !== 1) {
+ $content = $tokens[($nextArrow + 2)]['content'];
+ $error = 'Expected 1 space between double arrow and "%s"; %s found';
+ $data = array(
+ $content,
+ $spaceLength,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $nextArrow, 'SpaceAfterDoubleArrow', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($nextArrow + 1), ' ');
+ }
+ }
+ }//end if
+ }//end while
+
+ if ($valueCount > 0) {
+ $nestedParenthesis = false;
+ if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
+ $nested = $tokens[$stackPtr]['nested_parenthesis'];
+ $nestedParenthesis = array_pop($nested);
+ }
+
+ if ($nestedParenthesis === false
+ || $tokens[$nestedParenthesis]['line'] !== $tokens[$stackPtr]['line']
+ ) {
+ $error = 'Array with multiple values cannot be declared on a single line';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SingleLineNotAllowed');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->addNewline($arrayStart);
+ $phpcsFile->fixer->addNewlineBefore($arrayEnd);
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ return;
+ }
+
+ // We have a multiple value array that is inside a condition or
+ // function. Check its spacing is correct.
+ foreach ($commas as $comma) {
+ if ($tokens[($comma + 1)]['code'] !== T_WHITESPACE) {
+ $content = $tokens[($comma + 1)]['content'];
+ $error = 'Expected 1 space between comma and "%s"; 0 found';
+ $data = array($content);
+ $fix = $phpcsFile->addFixableError($error, $comma, 'NoSpaceAfterComma', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($comma, ' ');
+ }
+ } else {
+ $spaceLength = $tokens[($comma + 1)]['length'];
+ if ($spaceLength !== 1) {
+ $content = $tokens[($comma + 2)]['content'];
+ $error = 'Expected 1 space between comma and "%s"; %s found';
+ $data = array(
+ $content,
+ $spaceLength,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $comma, 'SpaceAfterComma', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($comma + 1), ' ');
+ }
+ }
+ }//end if
+
+ if ($tokens[($comma - 1)]['code'] === T_WHITESPACE) {
+ $content = $tokens[($comma - 2)]['content'];
+ $spaceLength = $tokens[($comma - 1)]['length'];
+ $error = 'Expected 0 spaces between "%s" and comma; %s found';
+ $data = array(
+ $content,
+ $spaceLength,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $comma, 'SpaceBeforeComma', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($comma - 1), '');
+ }
+ }
+ }//end foreach
+ }//end if
+
+ }//end processSingleLineArray()
+
+
+ /**
+ * 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.
+ *
+ * @return void
+ */
+ public function processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $keywordStart = $tokens[$stackPtr]['column'];
+
+ // Check the closing bracket is on a new line.
+ $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($arrayEnd - 1), $arrayStart, true);
+ if ($tokens[$lastContent]['line'] === $tokens[$arrayEnd]['line']) {
+ $error = 'Closing parenthesis of array declaration must be on a new line';
+ $fix = $phpcsFile->addFixableError($error, $arrayEnd, 'CloseBraceNewLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewlineBefore($arrayEnd);
+ }
+ } else if ($tokens[$arrayEnd]['column'] !== $keywordStart) {
+ // Check the closing bracket is lined up under the "a" in array.
+ $expected = ($keywordStart - 1);
+ $found = ($tokens[$arrayEnd]['column'] - 1);
+ $error = 'Closing parenthesis not aligned correctly; expected %s space(s) but found %s';
+ $data = array(
+ $expected,
+ $found,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $arrayEnd, 'CloseBraceNotAligned', $data);
+ if ($fix === true) {
+ if ($found === 0) {
+ $phpcsFile->fixer->addContent(($arrayEnd - 1), str_repeat(' ', $expected));
+ } else {
+ $phpcsFile->fixer->replaceToken(($arrayEnd - 1), str_repeat(' ', $expected));
+ }
+ }
+ }//end if
+
+ $keyUsed = false;
+ $singleUsed = false;
+ $indices = array();
+ $maxLength = 0;
+
+ if ($tokens[$stackPtr]['code'] === T_ARRAY) {
+ $lastToken = $tokens[$stackPtr]['parenthesis_opener'];
+ } else {
+ $lastToken = $stackPtr;
+ }
+
+ // Find all the double arrows that reside in this scope.
+ for ($nextToken = ($stackPtr + 1); $nextToken < $arrayEnd; $nextToken++) {
+ // Skip bracketed statements, like function calls.
+ if ($tokens[$nextToken]['code'] === T_OPEN_PARENTHESIS
+ && (isset($tokens[$nextToken]['parenthesis_owner']) === false
+ || $tokens[$nextToken]['parenthesis_owner'] !== $stackPtr)
+ ) {
+ $nextToken = $tokens[$nextToken]['parenthesis_closer'];
+ continue;
+ }
+
+ if ($tokens[$nextToken]['code'] === T_ARRAY
+ || $tokens[$nextToken]['code'] === T_OPEN_SHORT_ARRAY
+ || $tokens[$nextToken]['code'] === T_CLOSURE
+ ) {
+ // Let subsequent calls of this test handle nested arrays.
+ if ($tokens[$lastToken]['code'] !== T_DOUBLE_ARROW) {
+ $indices[] = array('value' => $nextToken);
+ $lastToken = $nextToken;
+ }
+
+ if ($tokens[$nextToken]['code'] === T_ARRAY) {
+ $nextToken = $tokens[$tokens[$nextToken]['parenthesis_opener']]['parenthesis_closer'];
+ } else if ($tokens[$nextToken]['code'] === T_OPEN_SHORT_ARRAY) {
+ $nextToken = $tokens[$nextToken]['bracket_closer'];
+ } else {
+ // T_CLOSURE.
+ $nextToken = $tokens[$nextToken]['scope_closer'];
+ }
+
+ $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($nextToken + 1), null, true);
+ if ($tokens[$nextToken]['code'] !== T_COMMA) {
+ $nextToken--;
+ } else {
+ $lastToken = $nextToken;
+ }
+
+ continue;
+ }//end if
+
+ if ($tokens[$nextToken]['code'] !== T_DOUBLE_ARROW
+ && $tokens[$nextToken]['code'] !== T_COMMA
+ ) {
+ continue;
+ }
+
+ $currentEntry = array();
+
+ if ($tokens[$nextToken]['code'] === T_COMMA) {
+ $stackPtrCount = 0;
+ if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
+ $stackPtrCount = count($tokens[$stackPtr]['nested_parenthesis']);
+ }
+
+ $commaCount = 0;
+ if (isset($tokens[$nextToken]['nested_parenthesis']) === true) {
+ $commaCount = count($tokens[$nextToken]['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,
+ // then there it is actually a comma used to separate arguments
+ // in a function call.
+ continue;
+ }
+
+ if ($keyUsed === true && $tokens[$lastToken]['code'] === T_COMMA) {
+ $error = 'No key specified for array entry; first entry specifies key';
+ $phpcsFile->addError($error, $nextToken, 'NoKeySpecified');
+ return;
+ }
+
+ if ($keyUsed === false) {
+ if ($tokens[($nextToken - 1)]['code'] === T_WHITESPACE) {
+ $content = $tokens[($nextToken - 2)]['content'];
+ if ($tokens[($nextToken - 1)]['content'] === $phpcsFile->eolChar) {
+ $spaceLength = 'newline';
+ } else {
+ $spaceLength = $tokens[($nextToken - 1)]['length'];
+ }
+
+ $error = 'Expected 0 spaces between "%s" and comma; %s found';
+ $data = array(
+ $content,
+ $spaceLength,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'SpaceBeforeComma', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($nextToken - 1), '');
+ }
+ }
+
+ $valueContent = $phpcsFile->findNext(
+ Tokens::$emptyTokens,
+ ($lastToken + 1),
+ $nextToken,
+ true
+ );
+
+ $indices[] = array('value' => $valueContent);
+ $singleUsed = true;
+ }//end if
+
+ $lastToken = $nextToken;
+ continue;
+ }//end if
+
+ if ($tokens[$nextToken]['code'] === T_DOUBLE_ARROW) {
+ if ($singleUsed === true) {
+ $error = 'Key specified for array entry; first entry has no key';
+ $phpcsFile->addError($error, $nextToken, 'KeySpecified');
+ return;
+ }
+
+ $currentEntry['arrow'] = $nextToken;
+ $keyUsed = true;
+
+ // Find the start of index that uses this double arrow.
+ $indexEnd = $phpcsFile->findPrevious(T_WHITESPACE, ($nextToken - 1), $arrayStart, true);
+ $indexStart = $phpcsFile->findStartOfStatement($indexEnd);
+
+ if ($indexStart === $indexEnd) {
+ $currentEntry['index'] = $indexEnd;
+ $currentEntry['index_content'] = $tokens[$indexEnd]['content'];
+ } else {
+ $currentEntry['index'] = $indexStart;
+ $currentEntry['index_content'] = $phpcsFile->getTokensAsString($indexStart, ($indexEnd - $indexStart + 1));
+ }
+
+ $indexLength = strlen($currentEntry['index_content']);
+ if ($maxLength < $indexLength) {
+ $maxLength = $indexLength;
+ }
+
+ // Find the value of this index.
+ $nextContent = $phpcsFile->findNext(
+ Tokens::$emptyTokens,
+ ($nextToken + 1),
+ $arrayEnd,
+ true
+ );
+
+ $currentEntry['value'] = $nextContent;
+ $indices[] = $currentEntry;
+ $lastToken = $nextToken;
+ }//end if
+ }//end for
+
+ // Check for mutli-line arrays that should be single-line.
+ $singleValue = false;
+
+ if (empty($indices) === true) {
+ $singleValue = true;
+ } else if (count($indices) === 1 && $tokens[$lastToken]['code'] === T_COMMA) {
+ // There may be another array value without a comma.
+ $exclude = Tokens::$emptyTokens;
+ $exclude[] = T_COMMA;
+ $nextContent = $phpcsFile->findNext($exclude, ($indices[0]['value'] + 1), $arrayEnd, true);
+ if ($nextContent === false) {
+ $singleValue = true;
+ }
+ }
+
+ if ($singleValue === true) {
+ // Array cannot be empty, so this is a multi-line array with
+ // a single value. It should be defined on single line.
+ $error = 'Multi-line array contains a single value; use single-line array instead';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'MultiLineNotAllowed');
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($arrayStart + 1); $i < $arrayEnd; $i++) {
+ if ($tokens[$i]['code'] !== T_WHITESPACE) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ for ($i = ($arrayEnd - 1); $i > $arrayStart; $i--) {
+ if ($tokens[$i]['code'] !== T_WHITESPACE) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ return;
+ }//end if
+
+ /*
+ This section checks for arrays that don't specify keys.
+
+ Arrays such as:
+ array(
+ 'aaa',
+ 'bbb',
+ 'd',
+ );
+ */
+
+ if ($keyUsed === false && empty($indices) === false) {
+ $count = count($indices);
+ $lastIndex = $indices[($count - 1)]['value'];
+
+ $trailingContent = $phpcsFile->findPrevious(
+ Tokens::$emptyTokens,
+ ($arrayEnd - 1),
+ $lastIndex,
+ true
+ );
+
+ if ($tokens[$trailingContent]['code'] !== T_COMMA) {
+ $phpcsFile->recordMetric($stackPtr, 'Array end comma', 'no');
+ $error = 'Comma required after last value in array declaration';
+ $fix = $phpcsFile->addFixableError($error, $trailingContent, 'NoCommaAfterLast');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($trailingContent, ',');
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Array end comma', 'yes');
+ }
+
+ $lastValueLine = false;
+ foreach ($indices as $value) {
+ if (empty($value['value']) === true) {
+ // Array was malformed and we couldn't figure out
+ // the array value correctly, so we have to ignore it.
+ // Other parts of this sniff will correct the error.
+ continue;
+ }
+
+ if ($lastValueLine !== false && $tokens[$value['value']]['line'] === $lastValueLine) {
+ $error = 'Each value in a multi-line array must be on a new line';
+ $fix = $phpcsFile->addFixableError($error, $value['value'], 'ValueNoNewline');
+ if ($fix === true) {
+ if ($tokens[($value['value'] - 1)]['code'] === T_WHITESPACE) {
+ $phpcsFile->fixer->replaceToken(($value['value'] - 1), '');
+ }
+
+ $phpcsFile->fixer->addNewlineBefore($value['value']);
+ }
+ } else if ($tokens[($value['value'] - 1)]['code'] === T_WHITESPACE) {
+ $expected = $keywordStart;
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $value['value'], true);
+ $found = ($tokens[$first]['column'] - 1);
+ if ($found !== $expected) {
+ $error = 'Array value not aligned correctly; expected %s spaces but found %s';
+ $data = array(
+ $expected,
+ $found,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $value['value'], 'ValueNotAligned', $data);
+ if ($fix === true) {
+ if ($found === 0) {
+ $phpcsFile->fixer->addContent(($value['value'] - 1), str_repeat(' ', $expected));
+ } else {
+ $phpcsFile->fixer->replaceToken(($value['value'] - 1), str_repeat(' ', $expected));
+ }
+ }
+ }
+ }//end if
+
+ $lastValueLine = $tokens[$value['value']]['line'];
+ }//end foreach
+ }//end if
+
+ /*
+ Below the actual indentation of the array is checked.
+ Errors will be thrown when a key is not aligned, when
+ a double arrow is not aligned, and when a value is not
+ aligned correctly.
+ If an error is found in one of the above areas, then errors
+ are not reported for the rest of the line to avoid reporting
+ spaces and columns incorrectly. Often fixing the first
+ problem will fix the other 2 anyway.
+
+ For example:
+
+ $a = array(
+ 'index' => '2',
+ );
+
+ or
+
+ $a = [
+ 'index' => '2',
+ ];
+
+ In this array, the double arrow is indented too far, but this
+ will also cause an error in the value's alignment. If the arrow were
+ to be moved back one space however, then both errors would be fixed.
+ */
+
+ $numValues = count($indices);
+
+ $indicesStart = ($keywordStart + 1);
+ $arrowStart = ($indicesStart + $maxLength + 1);
+ $valueStart = ($arrowStart + 3);
+ $indexLine = $tokens[$stackPtr]['line'];
+ $lastIndexLine = null;
+ foreach ($indices as $index) {
+ if (isset($index['index']) === false) {
+ // Array value only.
+ if ($tokens[$index['value']]['line'] === $tokens[$stackPtr]['line'] && $numValues > 1) {
+ $error = 'The first value in a multi-value array must be on a new line';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'FirstValueNoNewline');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewlineBefore($index['value']);
+ }
+ }
+
+ continue;
+ }
+
+ $lastIndexLine = $indexLine;
+ $indexLine = $tokens[$index['index']]['line'];
+
+ if ($indexLine === $tokens[$stackPtr]['line']) {
+ $error = 'The first index in a multi-value array must be on a new line';
+ $fix = $phpcsFile->addFixableError($error, $index['index'], 'FirstIndexNoNewline');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewlineBefore($index['index']);
+ }
+
+ continue;
+ }
+
+ if ($indexLine === $lastIndexLine) {
+ $error = 'Each index in a multi-line array must be on a new line';
+ $fix = $phpcsFile->addFixableError($error, $index['index'], 'IndexNoNewline');
+ if ($fix === true) {
+ if ($tokens[($index['index'] - 1)]['code'] === T_WHITESPACE) {
+ $phpcsFile->fixer->replaceToken(($index['index'] - 1), '');
+ }
+
+ $phpcsFile->fixer->addNewlineBefore($index['index']);
+ }
+
+ continue;
+ }
+
+ if ($tokens[$index['index']]['column'] !== $indicesStart) {
+ $expected = ($indicesStart - 1);
+ $found = ($tokens[$index['index']]['column'] - 1);
+ $error = 'Array key not aligned correctly; expected %s spaces but found %s';
+ $data = array(
+ $expected,
+ $found,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $index['index'], 'KeyNotAligned', $data);
+ if ($fix === true) {
+ if ($found === 0) {
+ $phpcsFile->fixer->addContent(($index['index'] - 1), str_repeat(' ', $expected));
+ } else {
+ $phpcsFile->fixer->replaceToken(($index['index'] - 1), str_repeat(' ', $expected));
+ }
+ }
+
+ continue;
+ }
+
+ if ($tokens[$index['arrow']]['column'] !== $arrowStart) {
+ $expected = ($arrowStart - (strlen($index['index_content']) + $tokens[$index['index']]['column']));
+ $found = ($tokens[$index['arrow']]['column'] - (strlen($index['index_content']) + $tokens[$index['index']]['column']));
+ $error = 'Array double arrow not aligned correctly; expected %s space(s) but found %s';
+ $data = array(
+ $expected,
+ $found,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $index['arrow'], 'DoubleArrowNotAligned', $data);
+ if ($fix === true) {
+ if ($found === 0) {
+ $phpcsFile->fixer->addContent(($index['arrow'] - 1), str_repeat(' ', $expected));
+ } else {
+ $phpcsFile->fixer->replaceToken(($index['arrow'] - 1), str_repeat(' ', $expected));
+ }
+ }
+
+ continue;
+ }
+
+ if ($tokens[$index['value']]['column'] !== $valueStart) {
+ $expected = ($valueStart - ($tokens[$index['arrow']]['length'] + $tokens[$index['arrow']]['column']));
+ $found = ($tokens[$index['value']]['column'] - ($tokens[$index['arrow']]['length'] + $tokens[$index['arrow']]['column']));
+ if ($found < 0) {
+ $found = 'newline';
+ }
+
+ $error = 'Array value not aligned correctly; expected %s space(s) but found %s';
+ $data = array(
+ $expected,
+ $found,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $index['arrow'], 'ValueNotAligned', $data);
+ if ($fix === true) {
+ if ($found === 'newline') {
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($index['value'] - 1), null, true);
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($prev + 1); $i < $index['value']; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->replaceToken(($index['value'] - 1), str_repeat(' ', $expected));
+ $phpcsFile->fixer->endChangeset();
+ } else if ($found === 0) {
+ $phpcsFile->fixer->addContent(($index['value'] - 1), str_repeat(' ', $expected));
+ } else {
+ $phpcsFile->fixer->replaceToken(($index['value'] - 1), str_repeat(' ', $expected));
+ }
+ }
+ }//end if
+
+ // Check each line ends in a comma.
+ $valueLine = $tokens[$index['value']]['line'];
+ $nextComma = false;
+ for ($i = $index['value']; $i < $arrayEnd; $i++) {
+ // Skip bracketed statements, like function calls.
+ if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
+ $i = $tokens[$i]['parenthesis_closer'];
+ $valueLine = $tokens[$i]['line'];
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_ARRAY) {
+ $i = $tokens[$tokens[$i]['parenthesis_opener']]['parenthesis_closer'];
+ $valueLine = $tokens[$i]['line'];
+ continue;
+ }
+
+ // Skip to the end of multi-line strings.
+ if (isset(Tokens::$stringTokens[$tokens[$i]['code']]) === true) {
+ $i = $phpcsFile->findNext($tokens[$i]['code'], ($i + 1), null, true);
+ $i--;
+ $valueLine = $tokens[$i]['line'];
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_START_HEREDOC || $tokens[$i]['code'] === T_START_NOWDOC) {
+ // Here/nowdoc closing tags must not be followed by a comma,
+ // so it must be on the next line.
+ $i = $tokens[$i]['scope_closer'];
+ $valueLine = ($tokens[$i]['line'] + 1);
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) {
+ $i = $tokens[$i]['bracket_closer'];
+ $valueLine = $tokens[$i]['line'];
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_CLOSURE) {
+ $i = $tokens[$i]['scope_closer'];
+ $valueLine = $tokens[$i]['line'];
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_COMMA) {
+ $nextComma = $i;
+ break;
+ }
+ }//end for
+
+ if ($nextComma === false || ($tokens[$nextComma]['line'] !== $valueLine)) {
+ $error = 'Each line in an array declaration must end in a comma';
+ $fix = $phpcsFile->addFixableError($error, $index['value'], 'NoComma');
+
+ if ($fix === true) {
+ // Find the end of the line and put a comma there.
+ for ($i = ($index['value'] + 1); $i < $arrayEnd; $i++) {
+ if ($tokens[$i]['line'] > $valueLine) {
+ break;
+ }
+ }
+
+ $phpcsFile->fixer->addContentBefore(($i - 1), ',');
+ }
+ }
+
+ // Check that there is no space before the comma.
+ if ($nextComma !== false && $tokens[($nextComma - 1)]['code'] === T_WHITESPACE) {
+ // Here/nowdoc closing tags must have the command on the next line.
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($nextComma - 1), null, true);
+ if ($tokens[$prev]['code'] !== T_END_HEREDOC && $tokens[$prev]['code'] !== T_END_NOWDOC) {
+ $content = $tokens[($nextComma - 2)]['content'];
+ $spaceLength = $tokens[($nextComma - 1)]['length'];
+ $error = 'Expected 0 spaces between "%s" and comma; %s found';
+ $data = array(
+ $content,
+ $spaceLength,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $nextComma, 'SpaceBeforeComma', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($nextComma - 1), '');
+ }
+ }
+ }
+ }//end foreach
+
+ }//end processMultiLineArray()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure there is a single blank line after the closing brace of a class definition.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+ use PHP_CodeSniffer\Sniffs\Sniff;
+ use PHP_CodeSniffer\Files\File;
+ use PHP_CodeSniffer\Util\Tokens;
+
+class ClassDefinitionClosingBraceSpaceSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_CLOSE_CURLY_BRACKET);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($next === false) {
+ return;
+ }
+
+ if ($tokens[$next]['code'] !== T_CLOSE_TAG) {
+ $found = (($tokens[$next]['line'] - $tokens[$stackPtr]['line']) - 1);
+ if ($found !== 1) {
+ $error = 'Expected one blank line after closing brace of class definition; %s found';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterClose', $data);
+
+ if ($fix === true) {
+ if ($found === 0) {
+ $phpcsFile->fixer->addNewline($stackPtr);
+ } else {
+ $nextContent = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($stackPtr + 1); $i < ($nextContent - 1); $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->addNewline($i);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ }//end if
+ }//end if
+
+ // Ignore nested style definitions from here on. The spacing before the closing brace
+ // (a single blank line) will be enforced by the above check, which ensures there is a
+ // blank line after the last nested class.
+ $found = $phpcsFile->findPrevious(
+ T_CLOSE_CURLY_BRACKET,
+ ($stackPtr - 1),
+ $tokens[$stackPtr]['bracket_opener']
+ );
+
+ if ($found !== false) {
+ return;
+ }
+
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($prev === false) {
+ return;
+ }
+
+ if ($tokens[$prev]['line'] === $tokens[$stackPtr]['line']) {
+ $error = 'Closing brace of class definition must be on new line';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'ContentBeforeClose');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewlineBefore($stackPtr);
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure there are no blank lines between the names of classes/IDs.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ClassDefinitionNameSpacingSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_CURLY_BRACKET);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Do not check nested style definitions as, for example, in @media style rules.
+ $nested = $phpcsFile->findNext(T_OPEN_CURLY_BRACKET, ($stackPtr + 1), $tokens[$stackPtr]['bracket_closer']);
+ if ($nested !== false) {
+ return;
+ }
+
+ // Find the first blank line before this opening brace, unless we get
+ // to another style definition, comment or the start of the file.
+ $endTokens = array(
+ T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
+ T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
+ T_OPEN_TAG => T_OPEN_TAG,
+ );
+ $endTokens += Tokens::$commentTokens;
+
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+
+ $foundContent = false;
+ $currentLine = $tokens[$prev]['line'];
+ for ($i = ($stackPtr - 1); $i >= 0; $i--) {
+ if (isset($endTokens[$tokens[$i]['code']]) === true) {
+ break;
+ }
+
+ if ($tokens[$i]['line'] === $currentLine) {
+ if ($tokens[$i]['code'] !== T_WHITESPACE) {
+ $foundContent = true;
+ }
+
+ continue;
+ }
+
+ // We changed lines.
+ if ($foundContent === false) {
+ // Before we throw an error, make sure we are not looking
+ // at a gap before the style definition.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, $i, null, true);
+ if ($prev !== false
+ && isset($endTokens[$tokens[$prev]['code']]) === false
+ ) {
+ $error = 'Blank lines are not allowed between class names';
+ $phpcsFile->addError($error, ($i + 1), 'BlankLinesFound');
+ }
+
+ break;
+ }
+
+ $foundContent = false;
+ $currentLine = $tokens[$i]['line'];
+ }//end for
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure a single space before, and a newline after, the class opening brace
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ClassDefinitionOpeningBraceSpaceSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_CURLY_BRACKET);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE) {
+ $error = 'Expected 1 space before opening brace of class definition; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoneBefore');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContentBefore($stackPtr, ' ');
+ }
+ } else {
+ $content = $tokens[($stackPtr - 1)]['content'];
+ if ($content !== ' ') {
+ if ($tokens[($stackPtr - 1)]['line'] < $tokens[$stackPtr]['line']) {
+ $length = 'newline';
+ } else {
+ $length = strlen($content);
+ if ($length === 1) {
+ $length = 'tab';
+ }
+ }
+
+ $error = 'Expected 1 space before opening brace of class definition; %s found';
+ $data = array($length);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Before', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
+ }
+ }
+ }//end if
+
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+ if ($next === false) {
+ return;
+ }
+
+ // Check for nested class definitions.
+ $nested = false;
+ $found = $phpcsFile->findNext(
+ T_OPEN_CURLY_BRACKET,
+ ($stackPtr + 1),
+ $tokens[$stackPtr]['bracket_closer']
+ );
+
+ if ($found !== false) {
+ $nested = true;
+ }
+
+ if ($tokens[$next]['line'] === $tokens[$stackPtr]['line']) {
+ $error = 'Opening brace should be the last content on the line';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'ContentBefore');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewline($stackPtr);
+ }
+ } else {
+ $foundLines = ($tokens[$next]['line'] - $tokens[$stackPtr]['line'] - 1);
+ if ($nested === true) {
+ if ($foundLines !== 1) {
+ $error = 'Expected 1 blank line after opening brace of nesting class definition; %s found';
+ $data = array($foundLines);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'AfterNesting', $data);
+
+ if ($fix === true) {
+ if ($foundLines === 0) {
+ $phpcsFile->fixer->addNewline($stackPtr);
+ } else {
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($stackPtr + 1); $i < ($next + 1); $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->addNewline($stackPtr);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ }//end if
+ }//end if
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure there is no space before a colon and one space after 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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ColonSpacingSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_COLON);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] !== T_STYLE) {
+ // The colon is not part of a style definition.
+ return;
+ }
+
+ if ($tokens[$prev]['content'] === 'progid') {
+ // Special case for IE filters.
+ return;
+ }
+
+ if ($tokens[($stackPtr - 1)]['code'] === T_WHITESPACE) {
+ $error = 'There must be no space before a colon in a style definition';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Before');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), '');
+ }
+ }
+
+ if ($tokens[($stackPtr + 1)]['code'] === T_SEMICOLON) {
+ // Empty style definition, ignore it.
+ return;
+ }
+
+ if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
+ $error = 'Expected 1 space after colon in style definition; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoneAfter');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($stackPtr, ' ');
+ }
+ } else {
+ $content = $tokens[($stackPtr + 1)]['content'];
+ if (strpos($content, $phpcsFile->eolChar) === false) {
+ $length = strlen($content);
+ if ($length !== 1) {
+ $error = 'Expected 1 space after colon in style definition; %s found';
+ $data = array($length);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'After', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+ } else {
+ $error = 'Expected 1 space after colon in style definition; newline found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'AfterNewline');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure colours are defined in upper-case and use shortcuts where possible.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ColourDefinitionSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_COLOUR);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $colour = $tokens[$stackPtr]['content'];
+
+ $expected = strtoupper($colour);
+ if ($colour !== $expected) {
+ $error = 'CSS colours must be defined in uppercase; expected %s but found %s';
+ $data = array(
+ $expected,
+ $colour,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotUpper', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, $expected);
+ }
+ }
+
+ // Now check if shorthand can be used.
+ if (strlen($colour) !== 7) {
+ return;
+ }
+
+ if ($colour{1} === $colour{2} && $colour{3} === $colour{4} && $colour{5} === $colour{6}) {
+ $expected = '#'.$colour{1}.$colour{3}.$colour{5};
+ $error = 'CSS colours must use shorthand if available; expected %s but found %s';
+ $data = array(
+ $expected,
+ $colour,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Shorthand', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, $expected);
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure that each style definition is on a line by itself.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DisallowMultipleStyleDefinitionsSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_STYLE);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $next = $phpcsFile->findNext(T_STYLE, ($stackPtr + 1));
+ if ($next === false) {
+ return;
+ }
+
+ if ($tokens[$next]['content'] === 'progid') {
+ // Special case for IE filters.
+ return;
+ }
+
+ if ($tokens[$next]['line'] === $tokens[$stackPtr]['line']) {
+ $error = 'Each style definition must be on a line by itself';
+ $fix = $phpcsFile->addFixableError($error, $next, 'Found');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewlineBefore($next);
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Check for duplicate class definitions that can be merged into one.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class DuplicateClassDefinitionSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Find the content of each class definition name.
+ $classNames = array();
+ $next = $phpcsFile->findNext(T_OPEN_CURLY_BRACKET, ($stackPtr + 1));
+ if ($next === false) {
+ // No class definitions in the file.
+ return;
+ }
+
+ // Save the class names in a "scope",
+ // to prevent false positives with @media blocks.
+ $scope = 'main';
+
+ $find = array(
+ T_CLOSE_CURLY_BRACKET,
+ T_OPEN_CURLY_BRACKET,
+ T_OPEN_TAG,
+ );
+
+ while ($next !== false) {
+ $prev = $phpcsFile->findPrevious($find, ($next - 1));
+
+ // Check if an inner block was closed.
+ $beforePrev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev - 1), null, true);
+ if ($beforePrev !== false
+ && $tokens[$beforePrev]['code'] === T_CLOSE_CURLY_BRACKET
+ ) {
+ $scope = 'main';
+ }
+
+ // Create a sorted name for the class so we can compare classes
+ // even when the individual names are all over the place.
+ $name = '';
+ for ($i = ($prev + 1); $i < $next; $i++) {
+ $name .= $tokens[$i]['content'];
+ }
+
+ $name = trim($name);
+ $name = str_replace("\n", ' ', $name);
+ $name = preg_replace('|[\s]+|', ' ', $name);
+ $name = str_replace(', ', ',', $name);
+
+ $names = explode(',', $name);
+ sort($names);
+ $name = implode(',', $names);
+
+ if ($name{0} === '@') {
+ // Media block has its own "scope".
+ $scope = $name;
+ } else if (isset($classNames[$scope][$name]) === true) {
+ $first = $classNames[$scope][$name];
+ $error = 'Duplicate class definition found; first defined on line %s';
+ $data = array($tokens[$first]['line']);
+ $phpcsFile->addError($error, $next, 'Found', $data);
+ } else {
+ $classNames[$scope][$name] = $next;
+ }
+
+ $next = $phpcsFile->findNext(T_OPEN_CURLY_BRACKET, ($next + 1));
+ }//end while
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Check for duplicate style definitions in the same class.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DuplicateStyleDefinitionSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_CURLY_BRACKET);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Find the content of each style definition name.
+ $styleNames = array();
+
+ $next = $stackPtr;
+ $end = $tokens[$stackPtr]['bracket_closer'];
+
+ do {
+ $next = $phpcsFile->findNext(array(T_STYLE, T_OPEN_CURLY_BRACKET), ($next + 1), $end);
+ if ($next === false) {
+ // Class definition is empty.
+ break;
+ }
+
+ if ($tokens[$next]['code'] === T_OPEN_CURLY_BRACKET) {
+ $next = $tokens[$next]['bracket_closer'];
+ continue;
+ }
+
+ $name = $tokens[$next]['content'];
+ if (isset($styleNames[$name]) === true) {
+ $first = $styleNames[$name];
+ $error = 'Duplicate style definition found; first defined on line %s';
+ $data = array($tokens[$first]['line']);
+ $phpcsFile->addError($error, $next, 'Found', $data);
+ } else {
+ $styleNames[$name] = $next;
+ }
+ } while ($next !== false);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure that class definitions are not empty.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class EmptyClassDefinitionSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_CURLY_BRACKET);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+
+ if ($next === false || $tokens[$next]['code'] === T_CLOSE_CURLY_BRACKET) {
+ $error = 'Class definition is empty';
+ $phpcsFile->addError($error, $stackPtr, 'Found');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure that style definitions are not empty.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class EmptyStyleDefinitionSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_STYLE);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $next = $phpcsFile->findNext(array(T_WHITESPACE, T_COLON), ($stackPtr + 1), null, true);
+
+ if ($next === false || $tokens[$next]['code'] === T_SEMICOLON || $tokens[$next]['line'] !== $tokens[$stackPtr]['line']) {
+ $error = 'Style definition is empty';
+ $phpcsFile->addError($error, $stackPtr, 'Found');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Bans the use of some styles, such as deprecated or browser-specific styles.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ForbiddenStylesSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+ /**
+ * A list of forbidden styles with their alternatives.
+ *
+ * The value is NULL if no alternative exists. i.e., the
+ * function should just not be used.
+ *
+ * @var array<string, string|null>
+ */
+ protected $forbiddenStyles = array(
+ '-moz-border-radius' => 'border-radius',
+ '-webkit-border-radius' => 'border-radius',
+ '-moz-border-radius-topleft' => 'border-top-left-radius',
+ '-moz-border-radius-topright' => 'border-top-right-radius',
+ '-moz-border-radius-bottomright' => 'border-bottom-right-radius',
+ '-moz-border-radius-bottomleft' => 'border-bottom-left-radius',
+ '-moz-box-shadow' => 'box-shadow',
+ '-webkit-box-shadow' => 'box-shadow',
+ );
+
+ /**
+ * A cache of forbidden style names, for faster lookups.
+ *
+ * @var string[]
+ */
+ protected $forbiddenStyleNames = array();
+
+ /**
+ * If true, forbidden styles will be considered regular expressions.
+ *
+ * @var boolean
+ */
+ protected $patternMatch = false;
+
+ /**
+ * If true, an error will be thrown; otherwise a warning.
+ *
+ * @var boolean
+ */
+ public $error = true;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ $this->forbiddenStyleNames = array_keys($this->forbiddenStyles);
+
+ if ($this->patternMatch === true) {
+ foreach ($this->forbiddenStyleNames as $i => $name) {
+ $this->forbiddenStyleNames[$i] = '/'.$name.'/i';
+ }
+ }
+
+ return array(T_STYLE);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $style = strtolower($tokens[$stackPtr]['content']);
+ $pattern = null;
+
+ if ($this->patternMatch === true) {
+ $count = 0;
+ $pattern = preg_replace(
+ $this->forbiddenStyleNames,
+ $this->forbiddenStyleNames,
+ $style,
+ 1,
+ $count
+ );
+
+ if ($count === 0) {
+ return;
+ }
+
+ // Remove the pattern delimiters and modifier.
+ $pattern = substr($pattern, 1, -2);
+ } else {
+ if (in_array($style, $this->forbiddenStyleNames) === false) {
+ return;
+ }
+ }//end if
+
+ $this->addError($phpcsFile, $stackPtr, $style, $pattern);
+
+ }//end process()
+
+
+ /**
+ * Generates the error or warning for this sniff.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the forbidden style
+ * in the token array.
+ * @param string $style The name of the forbidden style.
+ * @param string $pattern The pattern used for the match.
+ *
+ * @return void
+ */
+ protected function addError($phpcsFile, $stackPtr, $style, $pattern=null)
+ {
+ $data = array($style);
+ $error = 'The use of style %s is ';
+ if ($this->error === true) {
+ $type = 'Found';
+ $error .= 'forbidden';
+ } else {
+ $type = 'Discouraged';
+ $error .= 'discouraged';
+ }
+
+ if ($pattern === null) {
+ $pattern = $style;
+ }
+
+ if ($this->forbiddenStyles[$pattern] !== null) {
+ $data[] = $this->forbiddenStyles[$pattern];
+ if ($this->error === true) {
+ $fix = $phpcsFile->addFixableError($error.'; use %s instead', $stackPtr, $type.'WithAlternative', $data);
+ } else {
+ $fix = $phpcsFile->addFixableWarning($error.'; use %s instead', $stackPtr, $type.'WithAlternative', $data);
+ }
+
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, $this->forbiddenStyles[$pattern]);
+ }
+ } else {
+ if ($this->error === true) {
+ $phpcsFile->addError($error, $stackPtr, $type, $data);
+ } else {
+ $phpcsFile->addWarning($error, $stackPtr, $type, $data);
+ }
+ }
+
+ }//end addError()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures styles are indented 4 spaces.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class IndentationSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+ /**
+ * The number of spaces code should be indented.
+ *
+ * @var integer
+ */
+ public $indent = 4;
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $numTokens = (count($tokens) - 2);
+ $indentLevel = 0;
+ $nestingLevel = 0;
+ for ($i = 1; $i < $numTokens; $i++) {
+ if ($tokens[$i]['code'] === T_COMMENT) {
+ // Don't check the indent of comments.
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_OPEN_CURLY_BRACKET) {
+ $indentLevel++;
+
+ // Check for nested class definitions.
+ $found = $phpcsFile->findNext(
+ T_OPEN_CURLY_BRACKET,
+ ($i + 1),
+ $tokens[$i]['bracket_closer']
+ );
+
+ if ($found !== false) {
+ $nestingLevel = $indentLevel;
+ }
+ }
+
+ if (($tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET
+ && $tokens[$i]['line'] !== $tokens[($i - 1)]['line'])
+ || ($tokens[($i + 1)]['code'] === T_CLOSE_CURLY_BRACKET
+ && $tokens[$i]['line'] === $tokens[($i + 1)]['line'])
+ ) {
+ $indentLevel--;
+ if ($indentLevel === 0) {
+ $nestingLevel = 0;
+ }
+ }
+
+ if ($tokens[$i]['column'] !== 1
+ || $tokens[$i]['code'] === T_OPEN_CURLY_BRACKET
+ || $tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET
+ ) {
+ continue;
+ }
+
+ // We started a new line, so check indent.
+ if ($tokens[$i]['code'] === T_WHITESPACE) {
+ $content = str_replace($phpcsFile->eolChar, '', $tokens[$i]['content']);
+ $foundIndent = strlen($content);
+ } else {
+ $foundIndent = 0;
+ }
+
+ $expectedIndent = ($indentLevel * $this->indent);
+ if ($expectedIndent > 0
+ && strpos($tokens[$i]['content'], $phpcsFile->eolChar) !== false
+ ) {
+ if ($nestingLevel !== $indentLevel) {
+ $error = 'Blank lines are not allowed in class definitions';
+ $fix = $phpcsFile->addFixableError($error, $i, 'BlankLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+ }
+ } else if ($foundIndent !== $expectedIndent) {
+ $error = 'Line indented incorrectly; expected %s spaces, found %s';
+ $data = array(
+ $expectedIndent,
+ $foundIndent,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $i, 'Incorrect', $data);
+ if ($fix === true) {
+ $indent = str_repeat(' ', $expectedIndent);
+ if ($foundIndent === 0) {
+ $phpcsFile->fixer->addContentBefore($i, $indent);
+ } else {
+ $phpcsFile->fixer->replaceToken($i, $indent);
+ }
+ }
+ }//end if
+ }//end for
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure that all style definitions are in lowercase.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class LowercaseStyleDefinitionSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_CURLY_BRACKET);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $start = ($stackPtr + 1);
+ $end = ($tokens[$stackPtr]['bracket_closer'] - 1);
+ $inStyle = null;
+
+ for ($i = $start; $i <= $end; $i++) {
+ // Skip nested definitions as they are checked individually.
+ if ($tokens[$i]['code'] === T_OPEN_CURLY_BRACKET) {
+ $i = $tokens[$i]['bracket_closer'];
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_STYLE) {
+ $inStyle = $tokens[$i]['content'];
+ }
+
+ if ($tokens[$i]['code'] === T_SEMICOLON) {
+ $inStyle = null;
+ }
+
+ if ($inStyle === 'progid') {
+ // Special case for IE filters.
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_STYLE
+ || ($inStyle !== null
+ && $tokens[$i]['code'] === T_STRING)
+ ) {
+ $expected = strtolower($tokens[$i]['content']);
+ if ($expected !== $tokens[$i]['content']) {
+ $error = 'Style definitions must be lowercase; expected %s but found %s';
+ $data = array(
+ $expected,
+ $tokens[$i]['content'],
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $i, 'FoundUpper', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($i, $expected);
+ }
+ }
+ }
+ }//end for
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure that all style definitions have a colon.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class MissingColonSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_CURLY_BRACKET);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $lastLine = $tokens[$stackPtr]['line'];
+ $end = $tokens[$stackPtr]['bracket_closer'];
+ $endLine = $tokens[$end]['line'];
+
+ // Do not check nested style definitions as, for example, in @media style rules.
+ $nested = $phpcsFile->findNext(T_OPEN_CURLY_BRACKET, ($stackPtr + 1), $end);
+ if ($nested !== false) {
+ return;
+ }
+
+ $foundColon = false;
+ $foundString = false;
+ for ($i = ($stackPtr + 1); $i <= $end; $i++) {
+ if ($tokens[$i]['line'] !== $lastLine) {
+ // We changed lines.
+ if ($foundColon === false && $foundString !== false) {
+ // We didn't find a colon on the previous line.
+ $error = 'No style definition found on line; check for missing colon';
+ $phpcsFile->addError($error, $foundString, 'Found');
+ }
+
+ $foundColon = false;
+ $foundString = false;
+ $lastLine = $tokens[$i]['line'];
+ }
+
+ if ($tokens[$i]['code'] === T_STRING) {
+ $foundString = $i;
+ } else if ($tokens[$i]['code'] === T_COLON) {
+ $foundColon = $i;
+ }
+ }//end for
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure colour names are not used.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class NamedColoursSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * A list of named colours.
+ *
+ * This is the list of standard colours defined in the CSS spec.
+ *
+ * @var array
+ */
+ protected $colourNames = array(
+ 'aqua' => 'aqua',
+ 'black' => 'black',
+ 'blue' => 'blue',
+ 'fuchsia' => 'fuchsia',
+ 'gray' => 'gray',
+ 'green' => 'green',
+ 'lime' => 'lime',
+ 'maroon' => 'maroon',
+ 'navy' => 'navy',
+ 'olive' => 'olive',
+ 'orange' => 'orange',
+ 'purple' => 'purple',
+ 'red' => 'red',
+ 'silver' => 'silver',
+ 'teal' => 'teal',
+ 'white' => 'white',
+ 'yellow' => 'yellow',
+ );
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_STRING);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[($stackPtr - 1)]['code'] === T_HASH
+ || $tokens[($stackPtr - 1)]['code'] === T_STRING_CONCAT
+ ) {
+ // Class name.
+ return;
+ }
+
+ if (isset($this->colourNames[strtolower($tokens[$stackPtr]['content'])]) === true) {
+ $error = 'Named colours are forbidden; use hex, rgb, or rgba values instead';
+ $phpcsFile->addError($error, $stackPtr, 'Forbidden');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure that opacity values start with a 0 if it is not a whole number.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class OpacitySniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_STYLE);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['content'] !== 'opacity') {
+ return;
+ }
+
+ $next = $phpcsFile->findNext(array(T_COLON, T_WHITESPACE), ($stackPtr + 1), null, true);
+
+ if ($next === false
+ || ($tokens[$next]['code'] !== T_DNUMBER
+ && $tokens[$next]['code'] !== T_LNUMBER)
+ ) {
+ return;
+ }
+
+ $value = $tokens[$next]['content'];
+ if ($tokens[$next]['code'] === T_LNUMBER) {
+ if ($value !== '0' && $value !== '1') {
+ $error = 'Opacity values must be between 0 and 1';
+ $phpcsFile->addError($error, $next, 'Invalid');
+ }
+ } else {
+ if (strlen($value) > 3) {
+ $error = 'Opacity values must have a single value after the decimal point';
+ $phpcsFile->addError($error, $next, 'DecimalPrecision');
+ } else if ($value === '0.0' || $value === '1.0') {
+ $error = 'Opacity value does not require decimal point; use %s instead';
+ $data = array($value{0});
+ $fix = $phpcsFile->addFixableError($error, $next, 'PointNotRequired', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($next, $value{0});
+ }
+ } else if ($value{0} === '.') {
+ $error = 'Opacity values must not start with a decimal point; use 0%s instead';
+ $data = array($value);
+ $fix = $phpcsFile->addFixableError($error, $next, 'StartWithPoint', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($next, '0'.$value);
+ }
+ } else if ($value{0} !== '0') {
+ $error = 'Opacity values must be between 0 and 1';
+ $phpcsFile->addError($error, $next, 'Invalid');
+ }//end if
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure each style definition has a semi-colon and it is spaced correctly.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class SemicolonSpacingSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_STYLE);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $semicolon = $phpcsFile->findNext(T_SEMICOLON, ($stackPtr + 1));
+ if ($semicolon === false || $tokens[$semicolon]['line'] !== $tokens[$stackPtr]['line']) {
+ $error = 'Style definitions must end with a semicolon';
+ $phpcsFile->addError($error, $stackPtr, 'NotAtEnd');
+ return;
+ }
+
+ if ($tokens[($semicolon - 1)]['code'] === T_WHITESPACE) {
+ $length = strlen($tokens[($semicolon - 1)]['content']);
+ $error = 'Expected 0 spaces before semicolon in style definition; %s found';
+ $data = array($length);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceFound', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($semicolon - 1), '');
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure sizes are defined using shorthand notation where possible.
+ *
+ * @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\Standards\Squiz\Sniffs\CSS;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ShorthandSizeSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('CSS');
+
+ /**
+ * A list of styles that we shouldn't check.
+ *
+ * These have values that looks like sizes, but are not.
+ *
+ * @var array
+ */
+ protected $excludeStyles = array(
+ 'background-position' => 'background-position',
+ 'box-shadow' => 'box-shadow',
+ 'transform-origin' => 'transform-origin',
+ '-webkit-transform-origin' => '-webkit-transform-origin',
+ '-ms-transform-origin' => '-ms-transform-origin',
+ );
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_STYLE);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Some styles look like shorthand but are not actually a set of 4 sizes.
+ $style = strtolower($tokens[$stackPtr]['content']);
+ if (isset($this->excludeStyles[$style]) === true) {
+ return;
+ }
+
+ // Get the whole style content.
+ $end = $phpcsFile->findNext(T_SEMICOLON, ($stackPtr + 1));
+ $origContent = $phpcsFile->getTokensAsString(($stackPtr + 1), ($end - $stackPtr - 1));
+ $origContent = trim($origContent, ': ');
+
+ // Account for a !important annotation.
+ $content = $origContent;
+ if (substr($content, -10) === '!important') {
+ $content = substr($content, 0, -10);
+ $content = trim($content);
+ }
+
+ // Check if this style value is a set of numbers with optional prefixes.
+ $content = preg_replace('/\s+/', ' ', $content);
+ $values = array();
+ $num = preg_match_all(
+ '/([0-9]+)([a-zA-Z]{2}\s+|%\s+|\s+)/',
+ $content.' ',
+ $values,
+ PREG_SET_ORDER
+ );
+
+ // Only interested in styles that have multiple sizes defined.
+ if ($num < 2) {
+ return;
+ }
+
+ // Rebuild the content we matched to ensure we got everything.
+ $matched = '';
+ foreach ($values as $value) {
+ $matched .= $value[0];
+ }
+
+ if ($content !== trim($matched)) {
+ return;
+ }
+
+ if ($num === 3) {
+ $expected = trim($content.' '.$values[1][1].$values[1][2]);
+ $error = 'Shorthand syntax not allowed here; use %s instead';
+ $data = array($expected);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotAllowed', $data);
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ if (substr($origContent, -10) === '!important') {
+ $expected .= ' !important';
+ }
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 2), null, true);
+ $phpcsFile->fixer->replaceToken($next, $expected);
+ for ($next++; $next < $end; $next++) {
+ $phpcsFile->fixer->replaceToken($next, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ return;
+ }//end if
+
+ if ($num === 2) {
+ if ($values[0][0] !== $values[1][0]) {
+ // Both values are different, so it is already shorthand.
+ return;
+ }
+ } else if ($values[0][0] !== $values[2][0] || $values[1][0] !== $values[3][0]) {
+ // Can't shorthand this.
+ return;
+ }
+
+ if ($values[0][0] === $values[1][0]) {
+ // All values are the same.
+ $expected = $values[0][0];
+ } else {
+ $expected = $values[0][0].' '.$values[1][0];
+ }
+
+ $expected = preg_replace('/\s+/', ' ', trim($expected));
+
+ $error = 'Size definitions must use shorthand if available; expected "%s" but found "%s"';
+ $data = array(
+ $expected,
+ $content,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotUsed', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ if (substr($origContent, -10) === '!important') {
+ $expected .= ' !important';
+ }
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 2), null, true);
+ $phpcsFile->fixer->replaceToken($next, $expected);
+ for ($next++; $next < $end; $next++) {
+ $phpcsFile->fixer->replaceToken($next, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the declaration of the class and its inheritance is correct.
+ *
+ * @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\Standards\Squiz\Sniffs\Classes;
+
+use PHP_CodeSniffer\Standards\PSR2\Sniffs\Classes\ClassDeclarationSniff as PSR2ClassDeclarationSniff;
+use PHP_CodeSniffer\Files\File;
+
+class ClassDeclarationSniff extends PSR2ClassDeclarationSniff
+{
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ // We want all the errors from the PSR2 standard, plus some of our own.
+ parent::process($phpcsFile, $stackPtr);
+
+ $tokens = $phpcsFile->getTokens();
+
+ // Check that this is the only class or interface in the file.
+ $nextClass = $phpcsFile->findNext(array(T_CLASS, T_INTERFACE), ($stackPtr + 1));
+ if ($nextClass !== false) {
+ // We have another, so an error is thrown.
+ $error = 'Only one interface or class is allowed in a file';
+ $phpcsFile->addError($error, $nextClass, 'MultipleClasses');
+ }
+
+ }//end process()
+
+
+ /**
+ * Processes the opening section of a class declaration.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function processOpen(File $phpcsFile, $stackPtr)
+ {
+ parent::processOpen($phpcsFile, $stackPtr);
+
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[($stackPtr - 1)]['code'] === T_WHITESPACE) {
+ $prevContent = $tokens[($stackPtr - 1)]['content'];
+ if ($prevContent !== $phpcsFile->eolChar) {
+ $blankSpace = substr($prevContent, strpos($prevContent, $phpcsFile->eolChar));
+ $spaces = strlen($blankSpace);
+
+ if ($tokens[($stackPtr - 2)]['code'] !== T_ABSTRACT
+ && $tokens[($stackPtr - 2)]['code'] !== T_FINAL
+ ) {
+ if ($spaces !== 0) {
+ $type = strtolower($tokens[$stackPtr]['content']);
+ $error = 'Expected 0 spaces before %s keyword; %s found';
+ $data = array(
+ $type,
+ $spaces,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceBeforeKeyword', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), '');
+ }
+ }
+ }
+ }//end if
+ }//end if
+
+ }//end processOpen()
+
+
+ /**
+ * Processes the closing section of a class declaration.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function processClose(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ if (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ return;
+ }
+
+ $closeBrace = $tokens[$stackPtr]['scope_closer'];
+
+ // Check that the closing brace has one blank line after it.
+ for ($nextContent = ($closeBrace + 1); $nextContent < $phpcsFile->numTokens; $nextContent++) {
+ // Ignore comments on the same lines as the brace.
+ if ($tokens[$nextContent]['line'] === $tokens[$closeBrace]['line']
+ && ($tokens[$nextContent]['code'] === T_WHITESPACE
+ || $tokens[$nextContent]['code'] === T_COMMENT)
+ ) {
+ continue;
+ }
+
+ if ($tokens[$nextContent]['code'] !== T_WHITESPACE) {
+ break;
+ }
+ }
+
+ if ($nextContent === $phpcsFile->numTokens) {
+ // Ignore the line check as this is the very end of the file.
+ $difference = 1;
+ } else {
+ $difference = ($tokens[$nextContent]['line'] - $tokens[$closeBrace]['line'] - 1);
+ }
+
+ $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBrace - 1), $stackPtr, true);
+
+ if ($difference === -1
+ || $tokens[$lastContent]['line'] === $tokens[$closeBrace]['line']
+ ) {
+ $error = 'Closing %s brace must be on a line by itself';
+ $data = array($tokens[$stackPtr]['content']);
+ $fix = $phpcsFile->addFixableError($error, $closeBrace, 'CloseBraceSameLine', $data);
+ if ($fix === true) {
+ if ($difference === -1) {
+ $phpcsFile->fixer->addNewlineBefore($nextContent);
+ }
+
+ if ($tokens[$lastContent]['line'] === $tokens[$closeBrace]['line']) {
+ $phpcsFile->fixer->addNewlineBefore($closeBrace);
+ }
+ }
+ } else if ($tokens[($closeBrace - 1)]['code'] === T_WHITESPACE) {
+ $prevContent = $tokens[($closeBrace - 1)]['content'];
+ if ($prevContent !== $phpcsFile->eolChar) {
+ $blankSpace = substr($prevContent, strpos($prevContent, $phpcsFile->eolChar));
+ $spaces = strlen($blankSpace);
+ if ($spaces !== 0) {
+ if ($tokens[($closeBrace - 1)]['line'] !== $tokens[$closeBrace]['line']) {
+ $error = 'Expected 0 spaces before closing brace; newline found';
+ $phpcsFile->addError($error, $closeBrace, 'NewLineBeforeCloseBrace');
+ } else {
+ $error = 'Expected 0 spaces before closing brace; %s found';
+ $data = array($spaces);
+ $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpaceBeforeCloseBrace', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($closeBrace - 1), '');
+ }
+ }
+ }
+ }
+ }//end if
+
+ if ($difference !== -1 && $difference !== 1) {
+ $error = 'Closing brace of a %s must be followed by a single blank line; found %s';
+ $data = array(
+ $tokens[$stackPtr]['content'],
+ $difference,
+ );
+ $fix = $phpcsFile->addFixableError($error, $closeBrace, 'NewlinesAfterCloseBrace', $data);
+ if ($fix === true) {
+ if ($difference === 0) {
+ $first = $phpcsFile->findFirstOnLine(array(), $nextContent, true);
+ $phpcsFile->fixer->addNewlineBefore($first);
+ } else {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($closeBrace + 1); $i < $nextContent; $i++) {
+ if ($tokens[$i]['line'] <= ($tokens[$closeBrace]['line'] + 1)) {
+ continue;
+ } else if ($tokens[$i]['line'] === $tokens[$nextContent]['line']) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ }//end if
+
+ }//end processClose()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tests that the file name and the name of the class contained within the file match.
+ *
+ * @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\Standards\Squiz\Sniffs\Classes;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ClassFileNameSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_CLASS,
+ T_INTERFACE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $fullPath = basename($phpcsFile->getFilename());
+ $fileName = substr($fullPath, 0, strrpos($fullPath, '.'));
+ if ($fileName === '') {
+ // No filename probably means STDIN, so we can't do this check.
+ return;
+ }
+
+ $tokens = $phpcsFile->getTokens();
+ $decName = $phpcsFile->findNext(T_STRING, $stackPtr);
+
+ if ($tokens[$decName]['content'] !== $fileName) {
+ $error = '%s name doesn\'t match filename; expected "%s %s"';
+ $data = array(
+ ucfirst($tokens[$stackPtr]['content']),
+ $tokens[$stackPtr]['content'],
+ $fileName,
+ );
+ $phpcsFile->addError($error, $stackPtr, 'NoMatch', $data);
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures JS classes don't contain duplicate property names.
+ *
+ * @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\Standards\Squiz\Sniffs\Classes;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DuplicatePropertySniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OBJECT);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being processed.
+ * @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();
+
+ $properties = array();
+ $wantedTokens = array(
+ T_PROPERTY,
+ T_OBJECT,
+ );
+
+ $next = $phpcsFile->findNext($wantedTokens, ($stackPtr + 1), $tokens[$stackPtr]['bracket_closer']);
+ while ($next !== false && $next < $tokens[$stackPtr]['bracket_closer']) {
+ if ($tokens[$next]['code'] === T_OBJECT) {
+ // Skip nested objects.
+ $next = $tokens[$next]['bracket_closer'];
+ } else {
+ $propName = $tokens[$next]['content'];
+ if (isset($properties[$propName]) === true) {
+ $error = 'Duplicate property definition found for "%s"; previously defined on line %s';
+ $data = array(
+ $propName,
+ $tokens[$properties[$propName]]['line'],
+ );
+ $phpcsFile->addError($error, $next, 'Found', $data);
+ }
+
+ $properties[$propName] = $next;
+ }//end if
+
+ $next = $phpcsFile->findNext($wantedTokens, ($next + 1), $tokens[$stackPtr]['bracket_closer']);
+ }//end while
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures all class keywords are lowercase.
+ *
+ * @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\Standards\Squiz\Sniffs\Classes;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class LowercaseClassKeywordsSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_CLASS,
+ T_INTERFACE,
+ T_TRAIT,
+ T_EXTENDS,
+ T_IMPLEMENTS,
+ T_ABSTRACT,
+ T_FINAL,
+ T_VAR,
+ T_CONST,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $content = $tokens[$stackPtr]['content'];
+ if ($content !== strtolower($content)) {
+ $error = '%s keyword must be lowercase; expected "%s" but found "%s"';
+ $data = array(
+ strtoupper($content),
+ strtolower($content),
+ $content,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'FoundUppercase', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, strtolower($content));
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tests self member references.
+ *
+ * Verifies that :
+ * - self:: is used instead of Self::
+ * - self:: is used for local static member reference
+ * - self:: is used instead of self ::
+ *
+ * @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\Standards\Squiz\Sniffs\Classes;
+
+use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
+use PHP_CodeSniffer\Files\File;
+
+class SelfMemberReferenceSniff extends AbstractScopeSniff
+{
+
+
+ /**
+ * Constructs a Squiz_Sniffs_Classes_SelfMemberReferenceSniff.
+ */
+ public function __construct()
+ {
+ parent::__construct(array(T_CLASS), array(T_DOUBLE_COLON));
+
+ }//end __construct()
+
+
+ /**
+ * Processes the function tokens within the class.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The 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
+ */
+ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $calledClassName = ($stackPtr - 1);
+ if ($tokens[$calledClassName]['code'] === T_SELF) {
+ if ($tokens[$calledClassName]['content'] !== 'self') {
+ $error = 'Must use "self::" for local static member reference; found "%s::"';
+ $data = array($tokens[$calledClassName]['content']);
+ $fix = $phpcsFile->addFixableError($error, $calledClassName, 'IncorrectCase', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($calledClassName, 'self');
+ }
+
+ return;
+ }
+ } else if ($tokens[$calledClassName]['code'] === T_STRING) {
+ // If the class is called with a namespace prefix, build fully qualified
+ // namespace calls for both current scope class and requested class.
+ if ($tokens[($calledClassName - 1)]['code'] === T_NS_SEPARATOR) {
+ $declarationName = $this->getDeclarationNameWithNamespace($tokens, $calledClassName);
+ $declarationName = substr($declarationName, 1);
+ $fullQualifiedClassName = $this->getNamespaceOfScope($phpcsFile, $currScope);
+ if ($fullQualifiedClassName === '\\') {
+ $fullQualifiedClassName = '';
+ } else {
+ $fullQualifiedClassName .= '\\';
+ }
+
+ $fullQualifiedClassName .= $phpcsFile->getDeclarationName($currScope);
+ } else {
+ $declarationName = $phpcsFile->getDeclarationName($currScope);
+ $fullQualifiedClassName = $tokens[$calledClassName]['content'];
+ }
+
+ if ($declarationName === $fullQualifiedClassName) {
+ // Class name is the same as the current class, which is not allowed
+ // except if being used inside a closure.
+ if ($phpcsFile->hasCondition($stackPtr, T_CLOSURE) === false) {
+ $error = 'Must use "self::" for local static member reference';
+ $fix = $phpcsFile->addFixableError($error, $calledClassName, 'NotUsed');
+
+ if ($fix === true) {
+ $prev = $phpcsFile->findPrevious(array(T_NS_SEPARATOR, T_STRING), ($stackPtr - 1), null, true);
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($prev + 1); $i < $stackPtr; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->replaceToken($stackPtr, 'self::');
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ return;
+ }
+ }//end if
+ }//end if
+
+ if ($tokens[($stackPtr - 1)]['code'] === T_WHITESPACE) {
+ $found = strlen($tokens[($stackPtr - 1)]['content']);
+ $error = 'Expected 0 spaces before double colon; %s found';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $calledClassName, 'SpaceBefore', $data);
+
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), '');
+ }
+ }
+
+ if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
+ $found = strlen($tokens[($stackPtr + 1)]['content']);
+ $error = 'Expected 0 spaces after double colon; %s found';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $calledClassName, 'SpaceAfter', $data);
+
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), '');
+ }
+ }
+
+ }//end processTokenWithinScope()
+
+
+ /**
+ * 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.
+ *
+ * @return void
+ */
+ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
+ {
+
+ }//end processTokenOutsideScope()
+
+
+ /**
+ * Returns the declaration names for classes/interfaces/functions with a namespace.
+ *
+ * @param array $tokens Token stack for this file
+ * @param int $stackPtr The position where the namespace building will start.
+ *
+ * @return string
+ */
+ protected function getDeclarationNameWithNamespace(array $tokens, $stackPtr)
+ {
+ $nameParts = array();
+ $currentPointer = $stackPtr;
+ while ($tokens[$currentPointer]['code'] === T_NS_SEPARATOR
+ || $tokens[$currentPointer]['code'] === T_STRING
+ ) {
+ $nameParts[] = $tokens[$currentPointer]['content'];
+ $currentPointer--;
+ }
+
+ $nameParts = array_reverse($nameParts);
+ return implode('', $nameParts);
+
+ }//end getDeclarationNameWithNamespace()
+
+
+ /**
+ * Returns the namespace declaration of a file.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position where the search for the
+ * namespace declaration will start.
+ *
+ * @return string
+ */
+ protected function getNamespaceOfScope(File $phpcsFile, $stackPtr)
+ {
+ $namespace = '\\';
+ $namespaceDeclaration = $phpcsFile->findPrevious(T_NAMESPACE, $stackPtr);
+
+ if ($namespaceDeclaration !== false) {
+ $endOfNamespaceDeclaration = $phpcsFile->findNext(T_SEMICOLON, $namespaceDeclaration);
+ $namespace = $this->getDeclarationNameWithNamespace(
+ $phpcsFile->getTokens(),
+ ($endOfNamespaceDeclaration - 1)
+ );
+ }
+
+ return $namespace;
+
+ }//end getNamespaceOfScope()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures classes are in camel caps, and the first letter is capitalised.
+ *
+ * @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\Standards\Squiz\Sniffs\Classes;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Common;
+
+class ValidClassNameSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_CLASS,
+ T_INTERFACE,
+ T_TRAIT,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being processed.
+ * @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 (isset($tokens[$stackPtr]['scope_opener']) === false) {
+ $error = 'Possible parse error: %s missing opening or closing brace';
+ $data = array($tokens[$stackPtr]['content']);
+ $phpcsFile->addWarning($error, $stackPtr, 'MissingBrace', $data);
+ return;
+ }
+
+ // Determine the name of the class or interface. Note that we cannot
+ // simply look for the first T_STRING because a class name
+ // starting with the number will be multiple tokens.
+ $opener = $tokens[$stackPtr]['scope_opener'];
+ $nameStart = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), $opener, true);
+ $nameEnd = $phpcsFile->findNext(T_WHITESPACE, $nameStart, $opener);
+ if ($nameEnd === false) {
+ $name = $tokens[$nameStart]['content'];
+ } else {
+ $name = trim($phpcsFile->getTokensAsString($nameStart, ($nameEnd - $nameStart)));
+ }
+
+ // Check for camel caps format.
+ $valid = Common::isCamelCaps($name, true, true, false);
+ if ($valid === false) {
+ $type = ucfirst($tokens[$stackPtr]['content']);
+ $error = '%s name "%s" is not in camel caps format';
+ $data = array(
+ $type,
+ $name,
+ );
+ $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $data);
+ $phpcsFile->recordMetric($stackPtr, 'CamelCase class name', 'no');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'CamelCase class name', 'yes');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that block comments are used appropriately.
+ *
+ * @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\Standards\Squiz\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class BlockCommentSniff implements Sniff
+{
+
+ /**
+ * The --tab-width CLI value that is being used.
+ *
+ * @var integer
+ */
+ private $tabWidth = null;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_COMMENT,
+ T_DOC_COMMENT_OPEN_TAG,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ if ($this->tabWidth === null) {
+ if (isset($phpcsFile->config->tabWidth) === false || $phpcsFile->config->tabWidth === 0) {
+ // We have no idea how wide tabs are, so assume 4 spaces for fixing.
+ $this->tabWidth = 4;
+ } else {
+ $this->tabWidth = $phpcsFile->config->tabWidth;
+ }
+ }
+
+ $tokens = $phpcsFile->getTokens();
+
+ // If it's an inline comment, return.
+ if (substr($tokens[$stackPtr]['content'], 0, 2) !== '/*') {
+ return;
+ }
+
+ // If this is a function/class/interface doc block comment, skip it.
+ // We are only interested in inline doc block comments.
+ if ($tokens[$stackPtr]['code'] === T_DOC_COMMENT_OPEN_TAG) {
+ $nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+ $ignore = array(
+ T_CLASS => true,
+ T_INTERFACE => true,
+ T_TRAIT => true,
+ T_FUNCTION => true,
+ T_PUBLIC => true,
+ T_PRIVATE => true,
+ T_FINAL => true,
+ T_PROTECTED => true,
+ T_STATIC => true,
+ T_ABSTRACT => true,
+ T_CONST => true,
+ T_VAR => true,
+ );
+ if (isset($ignore[$tokens[$nextToken]['code']]) === true) {
+ return;
+ }
+
+ $prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$prevToken]['code'] === T_OPEN_TAG) {
+ return;
+ }
+
+ $error = 'Block comments must be started with /*';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'WrongStart');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, '/*');
+ }
+
+ $end = $tokens[$stackPtr]['comment_closer'];
+ if ($tokens[$end]['content'] !== '*/') {
+ $error = 'Block comments must be ended with */';
+ $fix = $phpcsFile->addFixableError($error, $end, 'WrongEnd');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($end, '*/');
+ }
+ }
+
+ return;
+ }//end if
+
+ $commentLines = array($stackPtr);
+ $nextComment = $stackPtr;
+ $lastLine = $tokens[$stackPtr]['line'];
+ $commentString = $tokens[$stackPtr]['content'];
+
+ // Construct the comment into an array.
+ while (($nextComment = $phpcsFile->findNext(T_WHITESPACE, ($nextComment + 1), null, true)) !== false) {
+ if ($tokens[$nextComment]['code'] !== $tokens[$stackPtr]['code']) {
+ // Found the next bit of code.
+ break;
+ }
+
+ if (($tokens[$nextComment]['line'] - 1) !== $lastLine) {
+ // Not part of the block.
+ break;
+ }
+
+ $lastLine = $tokens[$nextComment]['line'];
+ $commentLines[] = $nextComment;
+ $commentString .= $tokens[$nextComment]['content'];
+ if ($tokens[$nextComment]['code'] === T_DOC_COMMENT_CLOSE_TAG) {
+ break;
+ }
+ }
+
+ $commentText = str_replace($phpcsFile->eolChar, '', $commentString);
+ $commentText = trim($commentText, '/* ');
+ if ($commentText === '') {
+ $error = 'Empty block comment not allowed';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Empty');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken($stackPtr, '');
+ $lastToken = array_pop($commentLines);
+ for ($i = ($stackPtr + 1); $i <= $lastToken; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ return;
+ }
+
+ if (count($commentLines) === 1) {
+ $error = 'Single line block comment not allowed; use inline ("// text") comment instead';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SingleLine');
+ if ($fix === true) {
+ $comment = '// '.$commentText.$phpcsFile->eolChar;
+ $phpcsFile->fixer->replaceToken($stackPtr, $comment);
+ }
+
+ return;
+ }
+
+ $content = trim($tokens[$stackPtr]['content']);
+ if ($content !== '/*' && $content !== '/**') {
+ $error = 'Block comment text must start on a new line';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoNewLine');
+ if ($fix === true) {
+ $indent = '';
+ if ($tokens[($stackPtr - 1)]['code'] === T_WHITESPACE) {
+ if (isset($tokens[($stackPtr - 1)]['orig_content']) === true) {
+ $indent = $tokens[($stackPtr - 1)]['orig_content'];
+ } else {
+ $indent = $tokens[($stackPtr - 1)]['content'];
+ }
+ }
+
+ $comment = preg_replace(
+ '/^(\s*\/\*\*?)/',
+ '$1'.$phpcsFile->eolChar.$indent,
+ $tokens[$stackPtr]['content'],
+ 1
+ );
+ $phpcsFile->fixer->replaceToken($stackPtr, $comment);
+ }
+
+ return;
+ }//end if
+
+ $starColumn = ($tokens[$stackPtr]['column'] + 3);
+
+ // Make sure first line isn't blank.
+ if (trim($tokens[$commentLines[1]]['content']) === '') {
+ $error = 'Empty line not allowed at start of comment';
+ $fix = $phpcsFile->addFixableError($error, $commentLines[1], 'HasEmptyLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($commentLines[1], '');
+ }
+ } else {
+ // Check indentation of first line.
+ $content = $tokens[$commentLines[1]]['content'];
+ $commentText = ltrim($content);
+ $leadingSpace = (strlen($content) - strlen($commentText));
+ if ($leadingSpace !== $starColumn) {
+ $expected = $starColumn.' space';
+ if ($starColumn !== 1) {
+ $expected .= 's';
+ }
+
+ $data = array(
+ $expected,
+ $leadingSpace,
+ );
+
+ $error = 'First line of comment not aligned correctly; expected %s but found %s';
+ $fix = $phpcsFile->addFixableError($error, $commentLines[1], 'FirstLineIndent', $data);
+ if ($fix === true) {
+ if (isset($tokens[$commentLines[1]]['orig_content']) === true
+ && $tokens[$commentLines[1]]['orig_content'][0] === "\t"
+ ) {
+ // Line is indented using tabs.
+ $padding = str_repeat("\t", floor($starColumn / $this->tabWidth));
+ } else {
+ $padding = str_repeat(' ', $starColumn);
+ }
+
+ $phpcsFile->fixer->replaceToken($commentLines[1], $padding.ltrim($content));
+ }
+ }//end if
+
+ if (preg_match('/^\p{Ll}/u', $commentText) === 1) {
+ $error = 'Block comments must start with a capital letter';
+ $phpcsFile->addError($error, $commentLines[1], 'NoCapital');
+ }
+ }//end if
+
+ // Check that each line of the comment is indented past the star.
+ foreach ($commentLines as $line) {
+ $leadingSpace = (strlen($tokens[$line]['content']) - strlen(ltrim($tokens[$line]['content'])));
+ // First and last lines (comment opener and closer) are handled separately.
+ if ($line === $commentLines[(count($commentLines) - 1)] || $line === $commentLines[0]) {
+ continue;
+ }
+
+ // First comment line was handled above.
+ if ($line === $commentLines[1]) {
+ continue;
+ }
+
+ // If it's empty, continue.
+ if (trim($tokens[$line]['content']) === '') {
+ continue;
+ }
+
+ if ($leadingSpace < $starColumn) {
+ $expected = $starColumn.' space';
+ if ($starColumn !== 1) {
+ $expected .= 's';
+ }
+
+ $data = array(
+ $expected,
+ $leadingSpace,
+ );
+
+ $error = 'Comment line indented incorrectly; expected at least %s but found %s';
+ $fix = $phpcsFile->addFixableError($error, $line, 'LineIndent', $data);
+ if ($fix === true) {
+ if (isset($tokens[$line]['orig_content']) === true
+ && $tokens[$line]['orig_content'][0] === "\t"
+ ) {
+ // Line is indented using tabs.
+ $padding = str_repeat("\t", floor($starColumn / $this->tabWidth));
+ } else {
+ $padding = str_repeat(' ', $starColumn);
+ }
+
+ $phpcsFile->fixer->replaceToken($line, $padding.ltrim($tokens[$line]['content']));
+ }
+ }//end if
+ }//end foreach
+
+ // Finally, test the last line is correct.
+ $lastIndex = (count($commentLines) - 1);
+ $content = trim($tokens[$commentLines[$lastIndex]]['content']);
+ if ($content !== '*/' && $content !== '**/') {
+ $error = 'Comment closer must be on a new line';
+ $phpcsFile->addError($error, $commentLines[$lastIndex], 'CloserSameLine');
+ } else {
+ $content = $tokens[$commentLines[$lastIndex]]['content'];
+ $commentText = ltrim($content);
+ $leadingSpace = (strlen($content) - strlen($commentText));
+ if ($leadingSpace !== ($tokens[$stackPtr]['column'] - 1)) {
+ $expected = ($tokens[$stackPtr]['column'] - 1);
+ if ($expected === 1) {
+ $expected .= ' space';
+ } else {
+ $expected .= ' spaces';
+ }
+
+ $data = array(
+ $expected,
+ $leadingSpace,
+ );
+
+ $error = 'Last line of comment aligned incorrectly; expected %s but found %s';
+ $phpcsFile->addError($error, $commentLines[$lastIndex], 'LastLineIndent', $data);
+ }
+ }//end if
+
+ // Check that the lines before and after this comment are blank.
+ $contentBefore = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if (isset($tokens[$contentBefore]['scope_closer']) === true
+ && $tokens[$contentBefore]['scope_opener'] === $contentBefore
+ ) {
+ if (($tokens[$stackPtr]['line'] - $tokens[$contentBefore]['line']) !== 1) {
+ $error = 'Empty line not required before block comment';
+ $phpcsFile->addError($error, $stackPtr, 'HasEmptyLineBefore');
+ }
+ } else {
+ if (($tokens[$stackPtr]['line'] - $tokens[$contentBefore]['line']) < 2) {
+ $error = 'Empty line required before block comment';
+ $phpcsFile->addError($error, $stackPtr, 'NoEmptyLineBefore');
+ }
+ }
+
+ $commentCloser = $commentLines[$lastIndex];
+ $contentAfter = $phpcsFile->findNext(T_WHITESPACE, ($commentCloser + 1), null, true);
+ if ($contentAfter !== false && ($tokens[$contentAfter]['line'] - $tokens[$commentCloser]['line']) < 2) {
+ $error = 'Empty line required after block comment';
+ $phpcsFile->addError($error, $commentCloser, 'NoEmptyLineAfter');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Parses and verifies the class doc comment.
+ *
+ * Verifies that :
+ * <ul>
+ * <li>A class doc comment exists.</li>
+ * <li>The comment uses the correct docblock style.</li>
+ * <li>There are no blank lines after the class comment.</li>
+ * <li>No tags are used in the docblock.</li>
+ * </ul>
+ *
+ * @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\Standards\Squiz\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ClassCommentSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_CLASS);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $find = Tokens::$methodPrefixes;
+ $find[] = T_WHITESPACE;
+
+ $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
+ if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
+ && $tokens[$commentEnd]['code'] !== T_COMMENT
+ ) {
+ $phpcsFile->addError('Missing class doc comment', $stackPtr, 'Missing');
+ $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'no');
+ return;
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'yes');
+
+ if ($tokens[$commentEnd]['code'] === T_COMMENT) {
+ $phpcsFile->addError('You must use "/**" style comments for a class comment', $stackPtr, 'WrongStyle');
+ return;
+ }
+
+ if ($tokens[$commentEnd]['line'] !== ($tokens[$stackPtr]['line'] - 1)) {
+ $error = 'There must be no blank lines after the class comment';
+ $phpcsFile->addError($error, $commentEnd, 'SpacingAfter');
+ }
+
+ $commentStart = $tokens[$commentEnd]['comment_opener'];
+ foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
+ $error = '%s tag is not allowed in class comment';
+ $data = array($tokens[$tag]['content']);
+ $phpcsFile->addWarning($error, $tag, 'TagNotAllowed', $data);
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the //end ... comments on classes, interfaces and functions.
+ *
+ * @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\Standards\Squiz\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ClosingDeclarationCommentSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_FUNCTION,
+ T_CLASS,
+ T_INTERFACE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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_FUNCTION) {
+ $methodProps = $phpcsFile->getMethodProperties($stackPtr);
+
+ // Abstract methods do not require a closing comment.
+ if ($methodProps['is_abstract'] === true) {
+ return;
+ }
+
+ // If this function is in an interface then we don't require
+ // a closing comment.
+ if ($phpcsFile->hasCondition($stackPtr, T_INTERFACE) === true) {
+ return;
+ }
+
+ if (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ $error = 'Possible parse error: non-abstract method defined as abstract';
+ $phpcsFile->addWarning($error, $stackPtr, 'Abstract');
+ return;
+ }
+
+ $decName = $phpcsFile->getDeclarationName($stackPtr);
+ $comment = '//end '.$decName.'()';
+ } else if ($tokens[$stackPtr]['code'] === T_CLASS) {
+ $comment = '//end class';
+ } else {
+ $comment = '//end interface';
+ }//end if
+
+ if (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ $error = 'Possible parse error: %s missing opening or closing brace';
+ $data = array($tokens[$stackPtr]['content']);
+ $phpcsFile->addWarning($error, $stackPtr, 'MissingBrace', $data);
+ return;
+ }
+
+ $closingBracket = $tokens[$stackPtr]['scope_closer'];
+
+ if ($closingBracket === null) {
+ // Possible inline structure. Other tests will handle it.
+ return;
+ }
+
+ $data = array($comment);
+ if (isset($tokens[($closingBracket + 1)]) === false || $tokens[($closingBracket + 1)]['code'] !== T_COMMENT) {
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($closingBracket + 1), null, true);
+ if (rtrim($tokens[$next]['content']) === $comment) {
+ // The comment isn't really missing; it is just in the wrong place.
+ $fix = $phpcsFile->addFixableError('Expected %s directly after closing brace', $closingBracket, 'Misplaced', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($closingBracket + 1); $i < $next; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ // Just in case, because indentation fixes can add indents onto
+ // these comments and cause us to be unable to fix them.
+ $phpcsFile->fixer->replaceToken($next, $comment.$phpcsFile->eolChar);
+ $phpcsFile->fixer->endChangeset();
+ }
+ } else {
+ $fix = $phpcsFile->addFixableError('Expected %s', $closingBracket, 'Missing', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($closingBracket, '}'.$comment.$phpcsFile->eolChar);
+ }
+ }
+
+ return;
+ }//end if
+
+ if (rtrim($tokens[($closingBracket + 1)]['content']) !== $comment) {
+ $fix = $phpcsFile->addFixableError('Expected %s', $closingBracket, 'Incorrect', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($closingBracket + 1), $comment.$phpcsFile->eolChar);
+ }
+
+ return;
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tests that the stars in a doc comment align correctly.
+ *
+ * @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\Standards\Squiz\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class DocCommentAlignmentSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_DOC_COMMENT_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // We are only interested in function/class/interface doc block comments.
+ $ignore = Tokens::$emptyTokens;
+ if ($phpcsFile->tokenizerType === 'JS') {
+ $ignore[] = T_EQUAL;
+ $ignore[] = T_STRING;
+ $ignore[] = T_OBJECT_OPERATOR;
+ }
+
+ $nextToken = $phpcsFile->findNext($ignore, ($stackPtr + 1), null, true);
+ $ignore = array(
+ T_CLASS => true,
+ T_INTERFACE => true,
+ T_FUNCTION => true,
+ T_PUBLIC => true,
+ T_PRIVATE => true,
+ T_PROTECTED => true,
+ T_STATIC => true,
+ T_ABSTRACT => true,
+ T_PROPERTY => true,
+ T_OBJECT => true,
+ T_PROTOTYPE => true,
+ T_VAR => true,
+ );
+
+ if (isset($ignore[$tokens[$nextToken]['code']]) === false) {
+ // Could be a file comment.
+ $prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$prevToken]['code'] !== T_OPEN_TAG) {
+ return;
+ }
+ }
+
+ // There must be one space after each star (unless it is an empty comment line)
+ // and all the stars must be aligned correctly.
+ $requiredColumn = ($tokens[$stackPtr]['column'] + 1);
+ $endComment = $tokens[$stackPtr]['comment_closer'];
+ for ($i = ($stackPtr + 1); $i <= $endComment; $i++) {
+ if ($tokens[$i]['code'] !== T_DOC_COMMENT_STAR
+ && $tokens[$i]['code'] !== T_DOC_COMMENT_CLOSE_TAG
+ ) {
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_DOC_COMMENT_CLOSE_TAG) {
+ // Can't process the close tag if it is not the first thing on the line.
+ $prev = $phpcsFile->findPrevious(T_DOC_COMMENT_WHITESPACE, ($i - 1), $stackPtr, true);
+ if ($tokens[$prev]['line'] === $tokens[$i]['line']) {
+ continue;
+ }
+ }
+
+ if ($tokens[$i]['column'] !== $requiredColumn) {
+ $error = 'Expected %s space(s) before asterisk; %s found';
+ $data = array(
+ ($requiredColumn - 1),
+ ($tokens[$i]['column'] - 1),
+ );
+ $fix = $phpcsFile->addFixableError($error, $i, 'SpaceBeforeStar', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', ($requiredColumn - 1));
+ if ($tokens[$i]['column'] === 1) {
+ $phpcsFile->fixer->addContentBefore($i, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($i - 1), $padding);
+ }
+ }
+ }
+
+ if ($tokens[$i]['code'] !== T_DOC_COMMENT_STAR) {
+ continue;
+ }
+
+ if ($tokens[($i + 2)]['line'] !== $tokens[$i]['line']) {
+ // Line is empty.
+ continue;
+ }
+
+ if ($tokens[($i + 1)]['code'] !== T_DOC_COMMENT_WHITESPACE) {
+ $error = 'Expected 1 space after asterisk; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $i, 'NoSpaceAfterStar');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($i, ' ');
+ }
+ } else if ($tokens[($i + 2)]['code'] === T_DOC_COMMENT_TAG
+ && $tokens[($i + 1)]['content'] !== ' '
+ ) {
+ $error = 'Expected 1 space after asterisk; %s found';
+ $data = array(strlen($tokens[($i + 1)]['content']));
+ $fix = $phpcsFile->addFixableError($error, $i, 'SpaceAfterStar', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($i + 1), ' ');
+ }
+ }
+ }//end for
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks for empty catch clause without a comment.
+ *
+ * @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\Standards\Squiz\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class EmptyCatchCommentSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_CATCH);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document.
+ * @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();
+
+ $scopeStart = $tokens[$stackPtr]['scope_opener'];
+ $firstContent = $phpcsFile->findNext(T_WHITESPACE, ($scopeStart + 1), $tokens[$stackPtr]['scope_closer'], true);
+
+ if ($firstContent === false) {
+ $error = 'Empty CATCH statement must have a comment to explain why the exception is not handled';
+ $phpcsFile->addError($error, $scopeStart, 'Missing');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Parses and verifies the file doc comment.
+ *
+ * @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\Standards\Squiz\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class FileCommentSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return int
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $this->currentFile = $phpcsFile;
+
+ $tokens = $phpcsFile->getTokens();
+ $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+
+ if ($tokens[$commentStart]['code'] === T_COMMENT) {
+ $phpcsFile->addError('You must use "/**" style comments for a file comment', $commentStart, 'WrongStyle');
+ $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes');
+ return ($phpcsFile->numTokens + 1);
+ } else if ($commentStart === false || $tokens[$commentStart]['code'] !== T_DOC_COMMENT_OPEN_TAG) {
+ $phpcsFile->addError('Missing file doc comment', $stackPtr, 'Missing');
+ $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'no');
+ return ($phpcsFile->numTokens + 1);
+ }
+
+ $commentEnd = $tokens[$commentStart]['comment_closer'];
+
+ $nextToken = $phpcsFile->findNext(
+ T_WHITESPACE,
+ ($commentEnd + 1),
+ null,
+ true
+ );
+
+ $ignore = array(
+ T_CLASS,
+ T_INTERFACE,
+ T_TRAIT,
+ T_FUNCTION,
+ T_CLOSURE,
+ T_PUBLIC,
+ T_PRIVATE,
+ T_PROTECTED,
+ T_FINAL,
+ T_STATIC,
+ T_ABSTRACT,
+ T_CONST,
+ T_PROPERTY,
+ T_INCLUDE,
+ T_INCLUDE_ONCE,
+ T_REQUIRE,
+ T_REQUIRE_ONCE,
+ );
+
+ if (in_array($tokens[$nextToken]['code'], $ignore) === true) {
+ $phpcsFile->addError('Missing file doc comment', $stackPtr, 'Missing');
+ $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'no');
+ return ($phpcsFile->numTokens + 1);
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes');
+
+ // No blank line between the open tag and the file comment.
+ if ($tokens[$commentStart]['line'] > ($tokens[$stackPtr]['line'] + 1)) {
+ $error = 'There must be no blank lines before the file comment';
+ $phpcsFile->addError($error, $stackPtr, 'SpacingAfterOpen');
+ }
+
+ // Exactly one blank line after the file comment.
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($commentEnd + 1), null, true);
+ if ($tokens[$next]['line'] !== ($tokens[$commentEnd]['line'] + 2)) {
+ $error = 'There must be exactly one blank line after the file comment';
+ $phpcsFile->addError($error, $commentEnd, 'SpacingAfterComment');
+ }
+
+ // Required tags in correct order.
+ $required = array(
+ '@package' => true,
+ '@subpackage' => true,
+ '@author' => true,
+ '@copyright' => true,
+ );
+
+ $foundTags = array();
+ foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
+ $name = $tokens[$tag]['content'];
+ $isRequired = isset($required[$name]);
+
+ if ($isRequired === true && in_array($name, $foundTags) === true) {
+ $error = 'Only one %s tag is allowed in a file comment';
+ $data = array($name);
+ $phpcsFile->addError($error, $tag, 'Duplicate'.ucfirst(substr($name, 1)).'Tag', $data);
+ }
+
+ $foundTags[] = $name;
+
+ if ($isRequired === false) {
+ continue;
+ }
+
+ $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd);
+ if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) {
+ $error = 'Content missing for %s tag in file comment';
+ $data = array($name);
+ $phpcsFile->addError($error, $tag, 'Empty'.ucfirst(substr($name, 1)).'Tag', $data);
+ continue;
+ }
+
+ if ($name === '@author') {
+ if ($tokens[$string]['content'] !== 'Squiz Pty Ltd <products@squiz.net>') {
+ $error = 'Expected "Squiz Pty Ltd <products@squiz.net>" for author tag';
+ $fix = $phpcsFile->addFixableError($error, $tag, 'IncorrectAuthor');
+ if ($fix === true) {
+ $expected = 'Squiz Pty Ltd <products@squiz.net>';
+ $phpcsFile->fixer->replaceToken($string, $expected);
+ }
+ }
+ } else if ($name === '@copyright') {
+ if (preg_match('/^([0-9]{4})(-[0-9]{4})? (Squiz Pty Ltd \(ABN 77 084 670 600\))$/', $tokens[$string]['content']) === 0) {
+ $error = 'Expected "xxxx-xxxx Squiz Pty Ltd (ABN 77 084 670 600)" for copyright declaration';
+ $fix = $phpcsFile->addFixableError($error, $tag, 'IncorrectCopyright');
+ if ($fix === true) {
+ $matches = array();
+ preg_match('/^(([0-9]{4})(-[0-9]{4})?)?.*$/', $tokens[$string]['content'], $matches);
+ if (isset($matches[1]) === false) {
+ $matches[1] = date('Y');
+ }
+
+ $expected = $matches[1].' Squiz Pty Ltd (ABN 77 084 670 600)';
+ $phpcsFile->fixer->replaceToken($string, $expected);
+ }
+ }
+ }//end if
+ }//end foreach
+
+ // Check if the tags are in the correct position.
+ $pos = 0;
+ foreach ($required as $tag => $true) {
+ if (in_array($tag, $foundTags) === false) {
+ $error = 'Missing %s tag in file comment';
+ $data = array($tag);
+ $phpcsFile->addError($error, $commentEnd, 'Missing'.ucfirst(substr($tag, 1)).'Tag', $data);
+ }
+
+ if (isset($foundTags[$pos]) === false) {
+ break;
+ }
+
+ if ($foundTags[$pos] !== $tag) {
+ $error = 'The tag in position %s should be the %s tag';
+ $data = array(
+ ($pos + 1),
+ $tag,
+ );
+ $phpcsFile->addError($error, $tokens[$commentStart]['comment_tags'][$pos], ucfirst(substr($tag, 1)).'TagOrder', $data);
+ }
+
+ $pos++;
+ }//end foreach
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Parses and verifies the doc comments for functions.
+ *
+ * @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\Standards\Squiz\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Standards\PEAR\Sniffs\Commenting\FunctionCommentSniff as PEARFunctionCommentSniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Util\Common;
+
+class FunctionCommentSniff extends PEARFunctionCommentSniff
+{
+
+ /**
+ * The current PHP version.
+ *
+ * @var integer
+ */
+ private $phpVersion = null;
+
+
+ /**
+ * Process the return comment of this function comment.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $commentStart The position in the stack where the comment started.
+ *
+ * @return void
+ */
+ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Skip constructor and destructor.
+ $methodName = $phpcsFile->getDeclarationName($stackPtr);
+ $isSpecialMethod = ($methodName === '__construct' || $methodName === '__destruct');
+
+ $return = null;
+ foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
+ if ($tokens[$tag]['content'] === '@return') {
+ if ($return !== null) {
+ $error = 'Only 1 @return tag is allowed in a function comment';
+ $phpcsFile->addError($error, $tag, 'DuplicateReturn');
+ return;
+ }
+
+ $return = $tag;
+ }
+ }
+
+ if ($isSpecialMethod === true) {
+ return;
+ }
+
+ if ($return !== null) {
+ $content = $tokens[($return + 2)]['content'];
+ if (empty($content) === true || $tokens[($return + 2)]['code'] !== T_DOC_COMMENT_STRING) {
+ $error = 'Return type missing for @return tag in function comment';
+ $phpcsFile->addError($error, $return, 'MissingReturnType');
+ } else {
+ // Support both a return type and a description.
+ $split = preg_match('`^((?:\|?(?:array\([^\)]*\)|[\\\\a-z0-9\[\]]+))*)( .*)?`i', $content, $returnParts);
+ if (isset($returnParts[1]) === false) {
+ return;
+ }
+
+ $returnType = $returnParts[1];
+
+ // Check return type (can be multiple, separated by '|').
+ $typeNames = explode('|', $returnType);
+ $suggestedNames = array();
+ foreach ($typeNames as $i => $typeName) {
+ $suggestedName = Common::suggestType($typeName);
+ if (in_array($suggestedName, $suggestedNames) === false) {
+ $suggestedNames[] = $suggestedName;
+ }
+ }
+
+ $suggestedType = implode('|', $suggestedNames);
+ if ($returnType !== $suggestedType) {
+ $error = 'Expected "%s" but found "%s" for function return type';
+ $data = array(
+ $suggestedType,
+ $returnType,
+ );
+ $fix = $phpcsFile->addFixableError($error, $return, 'InvalidReturn', $data);
+ if ($fix === true) {
+ $replacement = $suggestedType;
+ if (empty($returnParts[2]) === false) {
+ $replacement .= $returnParts[2];
+ }
+
+ $phpcsFile->fixer->replaceToken(($return + 2), $replacement);
+ unset($replacement);
+ }
+ }
+
+ // If the return type is void, make sure there is
+ // no return statement in the function.
+ if ($returnType === 'void') {
+ if (isset($tokens[$stackPtr]['scope_closer']) === true) {
+ $endToken = $tokens[$stackPtr]['scope_closer'];
+ for ($returnToken = $stackPtr; $returnToken < $endToken; $returnToken++) {
+ if ($tokens[$returnToken]['code'] === T_CLOSURE
+ || $tokens[$returnToken]['code'] === T_ANON_CLASS
+ ) {
+ $returnToken = $tokens[$returnToken]['scope_closer'];
+ continue;
+ }
+
+ if ($tokens[$returnToken]['code'] === T_RETURN
+ || $tokens[$returnToken]['code'] === T_YIELD
+ || $tokens[$returnToken]['code'] === T_YIELD_FROM
+ ) {
+ break;
+ }
+ }
+
+ if ($returnToken !== $endToken) {
+ // If the function is not returning anything, just
+ // exiting, then there is no problem.
+ $semicolon = $phpcsFile->findNext(T_WHITESPACE, ($returnToken + 1), null, true);
+ if ($tokens[$semicolon]['code'] !== T_SEMICOLON) {
+ $error = 'Function return type is void, but function contains return statement';
+ $phpcsFile->addError($error, $return, 'InvalidReturnVoid');
+ }
+ }
+ }//end if
+ } else if ($returnType !== 'mixed' && in_array('void', $typeNames, true) === false) {
+ // If return type is not void, there needs to be a return statement
+ // somewhere in the function that returns something.
+ if (isset($tokens[$stackPtr]['scope_closer']) === true) {
+ $endToken = $tokens[$stackPtr]['scope_closer'];
+ for ($returnToken = $stackPtr; $returnToken < $endToken; $returnToken++) {
+ if ($tokens[$returnToken]['code'] === T_CLOSURE
+ || $tokens[$returnToken]['code'] === T_ANON_CLASS
+ ) {
+ $returnToken = $tokens[$returnToken]['scope_closer'];
+ continue;
+ }
+
+ if ($tokens[$returnToken]['code'] === T_RETURN
+ || $tokens[$returnToken]['code'] === T_YIELD
+ || $tokens[$returnToken]['code'] === T_YIELD_FROM
+ ) {
+ break;
+ }
+ }
+
+ if ($returnToken === $endToken) {
+ $error = 'Function return type is not void, but function has no return statement';
+ $phpcsFile->addError($error, $return, 'InvalidNoReturn');
+ } else {
+ $semicolon = $phpcsFile->findNext(T_WHITESPACE, ($returnToken + 1), null, true);
+ if ($tokens[$semicolon]['code'] === T_SEMICOLON) {
+ $error = 'Function return type is not void, but function is returning void here';
+ $phpcsFile->addError($error, $returnToken, 'InvalidReturnNotVoid');
+ }
+ }
+ }//end if
+ }//end if
+ }//end if
+ } else {
+ $error = 'Missing @return tag in function comment';
+ $phpcsFile->addError($error, $tokens[$commentStart]['comment_closer'], 'MissingReturn');
+ }//end if
+
+ }//end processReturn()
+
+
+ /**
+ * Process any throw tags that this function comment has.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $commentStart The position in the stack where the comment started.
+ *
+ * @return void
+ */
+ protected function processThrows(File $phpcsFile, $stackPtr, $commentStart)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $throws = array();
+ foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) {
+ if ($tokens[$tag]['content'] !== '@throws') {
+ continue;
+ }
+
+ $exception = null;
+ $comment = null;
+ if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) {
+ $matches = array();
+ preg_match('/([^\s]+)(?:\s+(.*))?/', $tokens[($tag + 2)]['content'], $matches);
+ $exception = $matches[1];
+ if (isset($matches[2]) === true && trim($matches[2]) !== '') {
+ $comment = $matches[2];
+ }
+ }
+
+ if ($exception === null) {
+ $error = 'Exception type and comment missing for @throws tag in function comment';
+ $phpcsFile->addError($error, $tag, 'InvalidThrows');
+ } else if ($comment === null) {
+ $error = 'Comment missing for @throws tag in function comment';
+ $phpcsFile->addError($error, $tag, 'EmptyThrows');
+ } else {
+ // Any strings until the next tag belong to this comment.
+ if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) {
+ $end = $tokens[$commentStart]['comment_tags'][($pos + 1)];
+ } else {
+ $end = $tokens[$commentStart]['comment_closer'];
+ }
+
+ for ($i = ($tag + 3); $i < $end; $i++) {
+ if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
+ $comment .= ' '.$tokens[$i]['content'];
+ }
+ }
+
+ // Starts with a capital letter and ends with a fullstop.
+ $firstChar = $comment{0};
+ if (strtoupper($firstChar) !== $firstChar) {
+ $error = '@throws tag comment must start with a capital letter';
+ $phpcsFile->addError($error, ($tag + 2), 'ThrowsNotCapital');
+ }
+
+ $lastChar = substr($comment, -1);
+ if ($lastChar !== '.') {
+ $error = '@throws tag comment must end with a full stop';
+ $phpcsFile->addError($error, ($tag + 2), 'ThrowsNoFullStop');
+ }
+ }//end if
+ }//end foreach
+
+ }//end processThrows()
+
+
+ /**
+ * Process the function parameter comments.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $commentStart The position in the stack where the comment started.
+ *
+ * @return void
+ */
+ protected function processParams(File $phpcsFile, $stackPtr, $commentStart)
+ {
+ if ($this->phpVersion === null) {
+ $this->phpVersion = Config::getConfigData('php_version');
+ if ($this->phpVersion === null) {
+ $this->phpVersion = PHP_VERSION_ID;
+ }
+ }
+
+ $tokens = $phpcsFile->getTokens();
+
+ $params = array();
+ $maxType = 0;
+ $maxVar = 0;
+ foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) {
+ if ($tokens[$tag]['content'] !== '@param') {
+ continue;
+ }
+
+ $type = '';
+ $typeSpace = 0;
+ $var = '';
+ $varSpace = 0;
+ $comment = '';
+ $commentLines = array();
+ if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) {
+ $matches = array();
+ preg_match('/([^$&.]+)(?:((?:\.\.\.)?(?:\$|&)[^\s]+)(?:(\s+)(.*))?)?/', $tokens[($tag + 2)]['content'], $matches);
+
+ if (empty($matches) === false) {
+ $typeLen = strlen($matches[1]);
+ $type = trim($matches[1]);
+ $typeSpace = ($typeLen - strlen($type));
+ $typeLen = strlen($type);
+ if ($typeLen > $maxType) {
+ $maxType = $typeLen;
+ }
+ }
+
+ if (isset($matches[2]) === true) {
+ $var = $matches[2];
+ $varLen = strlen($var);
+ if ($varLen > $maxVar) {
+ $maxVar = $varLen;
+ }
+
+ if (isset($matches[4]) === true) {
+ $varSpace = strlen($matches[3]);
+ $comment = $matches[4];
+ $commentLines[] = array(
+ 'comment' => $comment,
+ 'token' => ($tag + 2),
+ 'indent' => $varSpace,
+ );
+
+ // Any strings until the next tag belong to this comment.
+ if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) {
+ $end = $tokens[$commentStart]['comment_tags'][($pos + 1)];
+ } else {
+ $end = $tokens[$commentStart]['comment_closer'];
+ }
+
+ for ($i = ($tag + 3); $i < $end; $i++) {
+ if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
+ $indent = 0;
+ if ($tokens[($i - 1)]['code'] === T_DOC_COMMENT_WHITESPACE) {
+ $indent = strlen($tokens[($i - 1)]['content']);
+ }
+
+ $comment .= ' '.$tokens[$i]['content'];
+ $commentLines[] = array(
+ 'comment' => $tokens[$i]['content'],
+ 'token' => $i,
+ 'indent' => $indent,
+ );
+ }
+ }
+ } else {
+ $error = 'Missing parameter comment';
+ $phpcsFile->addError($error, $tag, 'MissingParamComment');
+ $commentLines[] = array('comment' => '');
+ }//end if
+ } else {
+ $error = 'Missing parameter name';
+ $phpcsFile->addError($error, $tag, 'MissingParamName');
+ }//end if
+ } else {
+ $error = 'Missing parameter type';
+ $phpcsFile->addError($error, $tag, 'MissingParamType');
+ }//end if
+
+ $params[] = array(
+ 'tag' => $tag,
+ 'type' => $type,
+ 'var' => $var,
+ 'comment' => $comment,
+ 'commentLines' => $commentLines,
+ 'type_space' => $typeSpace,
+ 'var_space' => $varSpace,
+ );
+ }//end foreach
+
+ $realParams = $phpcsFile->getMethodParameters($stackPtr);
+ $foundParams = array();
+
+ // We want to use ... for all variable length arguments, so added
+ // this prefix to the variable name so comparisons are easier.
+ foreach ($realParams as $pos => $param) {
+ if ($param['variable_length'] === true) {
+ $realParams[$pos]['name'] = '...'.$realParams[$pos]['name'];
+ }
+ }
+
+ foreach ($params as $pos => $param) {
+ // If the type is empty, the whole line is empty.
+ if ($param['type'] === '') {
+ continue;
+ }
+
+ // Check the param type value.
+ $typeNames = explode('|', $param['type']);
+ $suggestedTypeNames = array();
+
+ foreach ($typeNames as $typeName) {
+ $suggestedName = Common::suggestType($typeName);
+ $suggestedTypeNames[] = $suggestedName;
+
+ if (count($typeNames) > 1) {
+ continue;
+ }
+
+ // Check type hint for array and custom type.
+ $suggestedTypeHint = '';
+ if (strpos($suggestedName, 'array') !== false || substr($suggestedName, -2) === '[]') {
+ $suggestedTypeHint = 'array';
+ } else if (strpos($suggestedName, 'callable') !== false) {
+ $suggestedTypeHint = 'callable';
+ } else if (strpos($suggestedName, 'callback') !== false) {
+ $suggestedTypeHint = 'callable';
+ } else if (in_array($suggestedName, Common::$allowedTypes) === false) {
+ $suggestedTypeHint = $suggestedName;
+ }
+
+ if ($this->phpVersion >= 70000) {
+ if ($suggestedName === 'string') {
+ $suggestedTypeHint = 'string';
+ } else if ($suggestedName === 'int' || $suggestedName === 'integer') {
+ $suggestedTypeHint = 'int';
+ } else if ($suggestedName === 'float') {
+ $suggestedTypeHint = 'float';
+ } else if ($suggestedName === 'bool' || $suggestedName === 'boolean') {
+ $suggestedTypeHint = 'bool';
+ }
+ }
+
+ if ($suggestedTypeHint !== '' && isset($realParams[$pos]) === true) {
+ $typeHint = $realParams[$pos]['type_hint'];
+ if ($typeHint === '') {
+ $error = 'Type hint "%s" missing for %s';
+ $data = array(
+ $suggestedTypeHint,
+ $param['var'],
+ );
+
+ $errorCode = 'TypeHintMissing';
+ if ($suggestedTypeHint === 'string'
+ || $suggestedTypeHint === 'int'
+ || $suggestedTypeHint === 'float'
+ || $suggestedTypeHint === 'bool'
+ ) {
+ $errorCode = 'Scalar'.$errorCode;
+ }
+
+ $phpcsFile->addError($error, $stackPtr, $errorCode, $data);
+ } else if ($typeHint !== substr($suggestedTypeHint, (strlen($typeHint) * -1))) {
+ $error = 'Expected type hint "%s"; found "%s" for %s';
+ $data = array(
+ $suggestedTypeHint,
+ $typeHint,
+ $param['var'],
+ );
+ $phpcsFile->addError($error, $stackPtr, 'IncorrectTypeHint', $data);
+ }//end if
+ } else if ($suggestedTypeHint === '' && isset($realParams[$pos]) === true) {
+ $typeHint = $realParams[$pos]['type_hint'];
+ if ($typeHint !== '') {
+ $error = 'Unknown type hint "%s" found for %s';
+ $data = array(
+ $typeHint,
+ $param['var'],
+ );
+ $phpcsFile->addError($error, $stackPtr, 'InvalidTypeHint', $data);
+ }
+ }//end if
+ }//end foreach
+
+ $suggestedType = implode($suggestedTypeNames, '|');
+ if ($param['type'] !== $suggestedType) {
+ $error = 'Expected "%s" but found "%s" for parameter type';
+ $data = array(
+ $suggestedType,
+ $param['type'],
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $param['tag'], 'IncorrectParamVarName', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+
+ $content = $suggestedType;
+ $content .= str_repeat(' ', $param['type_space']);
+ $content .= $param['var'];
+ $content .= str_repeat(' ', $param['var_space']);
+ if (isset($param['commentLines'][0]) === true) {
+ $content .= $param['commentLines'][0]['comment'];
+ }
+
+ $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content);
+
+ // Fix up the indent of additional comment lines.
+ foreach ($param['commentLines'] as $lineNum => $line) {
+ if ($lineNum === 0
+ || $param['commentLines'][$lineNum]['indent'] === 0
+ ) {
+ continue;
+ }
+
+ $diff = (strlen($param['type']) - strlen($suggestedType));
+ $newIndent = ($param['commentLines'][$lineNum]['indent'] - $diff);
+ $phpcsFile->fixer->replaceToken(
+ ($param['commentLines'][$lineNum]['token'] - 1),
+ str_repeat(' ', $newIndent)
+ );
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+ }//end if
+
+ if ($param['var'] === '') {
+ continue;
+ }
+
+ $foundParams[] = $param['var'];
+
+ // Check number of spaces after the type.
+ $this->checkSpacingAfterParamType($phpcsFile, $param, $maxType);
+
+ // Make sure the param name is correct.
+ if (isset($realParams[$pos]) === true) {
+ $realName = $realParams[$pos]['name'];
+ if ($realName !== $param['var']) {
+ $code = 'ParamNameNoMatch';
+ $data = array(
+ $param['var'],
+ $realName,
+ );
+
+ $error = 'Doc comment for parameter %s does not match ';
+ if (strtolower($param['var']) === strtolower($realName)) {
+ $error .= 'case of ';
+ $code = 'ParamNameNoCaseMatch';
+ }
+
+ $error .= 'actual variable name %s';
+
+ $phpcsFile->addError($error, $param['tag'], $code, $data);
+ }
+ } else if (substr($param['var'], -4) !== ',...') {
+ // We must have an extra parameter comment.
+ $error = 'Superfluous parameter comment';
+ $phpcsFile->addError($error, $param['tag'], 'ExtraParamComment');
+ }//end if
+
+ if ($param['comment'] === '') {
+ continue;
+ }
+
+ // Check number of spaces after the var name.
+ $this->checkSpacingAfterParamName($phpcsFile, $param, $maxVar);
+
+ // Param comments must start with a capital letter and end with the full stop.
+ if (preg_match('/^(\p{Ll}|\P{L})/u', $param['comment']) === 1) {
+ $error = 'Parameter comment must start with a capital letter';
+ $phpcsFile->addError($error, $param['tag'], 'ParamCommentNotCapital');
+ }
+
+ $lastChar = substr($param['comment'], -1);
+ if ($lastChar !== '.') {
+ $error = 'Parameter comment must end with a full stop';
+ $phpcsFile->addError($error, $param['tag'], 'ParamCommentFullStop');
+ }
+ }//end foreach
+
+ $realNames = array();
+ foreach ($realParams as $realParam) {
+ $realNames[] = $realParam['name'];
+ }
+
+ // Report missing comments.
+ $diff = array_diff($realNames, $foundParams);
+ foreach ($diff as $neededParam) {
+ $error = 'Doc comment for parameter "%s" missing';
+ $data = array($neededParam);
+ $phpcsFile->addError($error, $commentStart, 'MissingParamTag', $data);
+ }
+
+ }//end processParams()
+
+
+ /**
+ * Check the spacing after the type of a parameter.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $param The parameter to be checked.
+ * @param int $maxType The maxlength of the longest parameter type.
+ * @param int $spacing The number of spaces to add after the type.
+ *
+ * @return void
+ */
+ protected function checkSpacingAfterParamType(File $phpcsFile, $param, $maxType, $spacing=1)
+ {
+ // Check number of spaces after the type.
+ $spaces = ($maxType - strlen($param['type']) + $spacing);
+ if ($param['type_space'] !== $spaces) {
+ $error = 'Expected %s spaces after parameter type; %s found';
+ $data = array(
+ $spaces,
+ $param['type_space'],
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $param['tag'], 'SpacingAfterParamType', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+
+ $content = $param['type'];
+ $content .= str_repeat(' ', $spaces);
+ $content .= $param['var'];
+ $content .= str_repeat(' ', $param['var_space']);
+ $content .= $param['commentLines'][0]['comment'];
+ $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content);
+
+ // Fix up the indent of additional comment lines.
+ foreach ($param['commentLines'] as $lineNum => $line) {
+ if ($lineNum === 0
+ || $param['commentLines'][$lineNum]['indent'] === 0
+ ) {
+ continue;
+ }
+
+ $diff = ($param['type_space'] - $spaces);
+ $newIndent = ($param['commentLines'][$lineNum]['indent'] - $diff);
+ $phpcsFile->fixer->replaceToken(
+ ($param['commentLines'][$lineNum]['token'] - 1),
+ str_repeat(' ', $newIndent)
+ );
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+ }//end if
+
+ }//end checkSpacingAfterParamType()
+
+
+ /**
+ * Check the spacing after the name of a parameter.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $param The parameter to be checked.
+ * @param int $maxVar The maxlength of the longest parameter name.
+ * @param int $spacing The number of spaces to add after the type.
+ *
+ * @return void
+ */
+ protected function checkSpacingAfterParamName(File $phpcsFile, $param, $maxVar, $spacing=1)
+ {
+ // Check number of spaces after the var name.
+ $spaces = ($maxVar - strlen($param['var']) + $spacing);
+ if ($param['var_space'] !== $spaces) {
+ $error = 'Expected %s spaces after parameter name; %s found';
+ $data = array(
+ $spaces,
+ $param['var_space'],
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $param['tag'], 'SpacingAfterParamName', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+
+ $content = $param['type'];
+ $content .= str_repeat(' ', $param['type_space']);
+ $content .= $param['var'];
+ $content .= str_repeat(' ', $spaces);
+ $content .= $param['commentLines'][0]['comment'];
+ $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content);
+
+ // Fix up the indent of additional comment lines.
+ foreach ($param['commentLines'] as $lineNum => $line) {
+ if ($lineNum === 0
+ || $param['commentLines'][$lineNum]['indent'] === 0
+ ) {
+ continue;
+ }
+
+ $diff = ($param['var_space'] - $spaces);
+ $newIndent = ($param['commentLines'][$lineNum]['indent'] - $diff);
+ if ($newIndent <= 0) {
+ continue;
+ }
+
+ $phpcsFile->fixer->replaceToken(
+ ($param['commentLines'][$lineNum]['token'] - 1),
+ str_repeat(' ', $newIndent)
+ );
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+ }//end if
+
+ }//end checkSpacingAfterParamName()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that a @throws tag exists for each exception type a function throws.
+ *
+ * @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\Standards\Squiz\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class FunctionCommentThrowTagSniff extends AbstractScopeSniff
+{
+
+
+ /**
+ * Constructs a Squiz_Sniffs_Commenting_FunctionCommentThrowTagSniff.
+ */
+ public function __construct()
+ {
+ parent::__construct(array(T_FUNCTION), array(T_THROW));
+
+ }//end __construct()
+
+
+ /**
+ * Processes the function tokens within the class.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The 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
+ */
+ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
+ {
+ // Is this the first throw token within the current function scope?
+ // If so, we have to validate other throw tokens within the same scope.
+ $previousThrow = $phpcsFile->findPrevious(T_THROW, ($stackPtr - 1), $currScope);
+ if ($previousThrow !== false) {
+ return;
+ }
+
+ $tokens = $phpcsFile->getTokens();
+
+ $find = Tokens::$methodPrefixes;
+ $find[] = T_WHITESPACE;
+
+ $commentEnd = $phpcsFile->findPrevious($find, ($currScope - 1), null, true);
+ if ($tokens[$commentEnd]['code'] === T_COMMENT) {
+ // Function is using the wrong type of comment.
+ return;
+ }
+
+ if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
+ && $tokens[$commentEnd]['code'] !== T_COMMENT
+ ) {
+ // Function doesn't have a doc comment.
+ return;
+ }
+
+ $currScopeEnd = $tokens[$currScope]['scope_closer'];
+
+ // Find all the exception type token within the current scope.
+ $thrownExceptions = array();
+ $currPos = $stackPtr;
+ $foundThrows = false;
+ while ($currPos < $currScopeEnd && $currPos !== false) {
+ if ($phpcsFile->hasCondition($currPos, T_CLOSURE) === false) {
+ $foundThrows = true;
+
+ /*
+ If we can't find a NEW, we are probably throwing
+ a variable.
+
+ If we're throwing the same variable as the exception container
+ from the nearest 'catch' block, we take that exception, as it is
+ likely to be a re-throw.
+
+ If we can't find a matching catch block, or the variable name
+ is different, it's probably a different variable, so we ignore it,
+ but they still need to provide at least one @throws tag, even through we
+ don't know the exception class.
+ */
+
+ $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($currPos + 1), null, true);
+ if ($tokens[$nextToken]['code'] === T_NEW) {
+ $currException = $phpcsFile->findNext(
+ array(
+ T_NS_SEPARATOR,
+ T_STRING,
+ ),
+ $currPos,
+ $currScopeEnd,
+ false,
+ null,
+ true
+ );
+
+ if ($currException !== false) {
+ $endException = $phpcsFile->findNext(
+ array(
+ T_NS_SEPARATOR,
+ T_STRING,
+ ),
+ ($currException + 1),
+ $currScopeEnd,
+ true,
+ null,
+ true
+ );
+
+ if ($endException === false) {
+ $thrownExceptions[] = $tokens[$currException]['content'];
+ } else {
+ $thrownExceptions[] = $phpcsFile->getTokensAsString($currException, ($endException - $currException));
+ }
+ }//end if
+ } else if ($tokens[$nextToken]['code'] === T_VARIABLE) {
+ // Find the nearest catch block in this scope and, if the caught var
+ // matches our rethrown var, use the exception types being caught as
+ // exception types that are being thrown as well.
+ $catch = $phpcsFile->findPrevious(
+ T_CATCH,
+ $currPos,
+ $tokens[$currScope]['scope_opener'],
+ false,
+ null,
+ false
+ );
+
+ if ($catch !== false) {
+ $thrownVar = $phpcsFile->findPrevious(
+ T_VARIABLE,
+ ($tokens[$catch]['parenthesis_closer'] - 1),
+ $tokens[$catch]['parenthesis_opener']
+ );
+
+ if ($tokens[$thrownVar]['content'] === $tokens[$nextToken]['content']) {
+ $exceptions = explode('|', $phpcsFile->getTokensAsString(($tokens[$catch]['parenthesis_opener'] + 1), ($thrownVar - $tokens[$catch]['parenthesis_opener'] - 1)));
+ foreach ($exceptions as $exception) {
+ $thrownExceptions[] = trim($exception);
+ }
+ }
+ }
+ }//end if
+ }//end if
+
+ $currPos = $phpcsFile->findNext(T_THROW, ($currPos + 1), $currScopeEnd);
+ }//end while
+
+ if ($foundThrows === false) {
+ return;
+ }
+
+ // Only need one @throws tag for each type of exception thrown.
+ $thrownExceptions = array_unique($thrownExceptions);
+
+ $throwTags = array();
+ $commentStart = $tokens[$commentEnd]['comment_opener'];
+ foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
+ if ($tokens[$tag]['content'] !== '@throws') {
+ continue;
+ }
+
+ if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) {
+ $exception = $tokens[($tag + 2)]['content'];
+ $space = strpos($exception, ' ');
+ if ($space !== false) {
+ $exception = substr($exception, 0, $space);
+ }
+
+ $throwTags[$exception] = true;
+ }
+ }
+
+ if (empty($throwTags) === true) {
+ $error = 'Missing @throws tag in function comment';
+ $phpcsFile->addError($error, $commentEnd, 'Missing');
+ return;
+ } else if (empty($thrownExceptions) === true) {
+ // If token count is zero, it means that only variables are being
+ // thrown, so we need at least one @throws tag (checked above).
+ // Nothing more to do.
+ return;
+ }
+
+ // Make sure @throws tag count matches thrown count.
+ $thrownCount = count($thrownExceptions);
+ $tagCount = count($throwTags);
+ if ($thrownCount !== $tagCount) {
+ $error = 'Expected %s @throws tag(s) in function comment; %s found';
+ $data = array(
+ $thrownCount,
+ $tagCount,
+ );
+ $phpcsFile->addError($error, $commentEnd, 'WrongNumber', $data);
+ return;
+ }
+
+ foreach ($thrownExceptions as $throw) {
+ if (isset($throwTags[$throw]) === false) {
+ $error = 'Missing @throws tag for "%s" exception';
+ $data = array($throw);
+ $phpcsFile->addError($error, $commentEnd, 'Missing', $data);
+ }
+ }
+
+ }//end processTokenWithinScope()
+
+
+ /**
+ * 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.
+ *
+ * @return void
+ */
+ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
+ {
+
+ }//end processTokenOutsideScope()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that there is adequate spacing between comments.
+ *
+ * @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\Standards\Squiz\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class InlineCommentSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_COMMENT,
+ T_DOC_COMMENT_OPEN_TAG,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 this is a function/class/interface doc block comment, skip it.
+ // We are only interested in inline doc block comments, which are
+ // not allowed.
+ if ($tokens[$stackPtr]['code'] === T_DOC_COMMENT_OPEN_TAG) {
+ $nextToken = $phpcsFile->findNext(
+ Tokens::$emptyTokens,
+ ($stackPtr + 1),
+ null,
+ true
+ );
+
+ $ignore = array(
+ T_CLASS,
+ T_INTERFACE,
+ T_TRAIT,
+ T_FUNCTION,
+ T_CLOSURE,
+ T_PUBLIC,
+ T_PRIVATE,
+ T_PROTECTED,
+ T_FINAL,
+ T_STATIC,
+ T_ABSTRACT,
+ T_CONST,
+ T_PROPERTY,
+ T_INCLUDE,
+ T_INCLUDE_ONCE,
+ T_REQUIRE,
+ T_REQUIRE_ONCE,
+ );
+
+ if (in_array($tokens[$nextToken]['code'], $ignore) === true) {
+ return;
+ }
+
+ if ($phpcsFile->tokenizerType === 'JS') {
+ // We allow block comments if a function or object
+ // is being assigned to a variable.
+ $ignore = Tokens::$emptyTokens;
+ $ignore[] = T_EQUAL;
+ $ignore[] = T_STRING;
+ $ignore[] = T_OBJECT_OPERATOR;
+ $nextToken = $phpcsFile->findNext($ignore, ($nextToken + 1), null, true);
+ if ($tokens[$nextToken]['code'] === T_FUNCTION
+ || $tokens[$nextToken]['code'] === T_CLOSURE
+ || $tokens[$nextToken]['code'] === T_OBJECT
+ || $tokens[$nextToken]['code'] === T_PROTOTYPE
+ ) {
+ return;
+ }
+ }
+
+ $prevToken = $phpcsFile->findPrevious(
+ Tokens::$emptyTokens,
+ ($stackPtr - 1),
+ null,
+ true
+ );
+
+ if ($tokens[$prevToken]['code'] === T_OPEN_TAG) {
+ return;
+ }
+
+ if ($tokens[$stackPtr]['content'] === '/**') {
+ $error = 'Inline doc block comments are not allowed; use "/* Comment */" or "// Comment" instead';
+ $phpcsFile->addError($error, $stackPtr, 'DocBlock');
+ }
+ }//end if
+
+ if ($tokens[$stackPtr]['content']{0} === '#') {
+ $error = 'Perl-style comments are not allowed; use "// Comment" instead';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'WrongStyle');
+ if ($fix === true) {
+ $comment = ltrim($tokens[$stackPtr]['content'], "# \t");
+ $phpcsFile->fixer->replaceToken($stackPtr, "// $comment");
+ }
+ }
+
+ // We don't want end of block comments. If the last comment is a closing
+ // curly brace.
+ $previousContent = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($tokens[$previousContent]['line'] === $tokens[$stackPtr]['line']) {
+ if ($tokens[$previousContent]['code'] === T_CLOSE_CURLY_BRACKET) {
+ return;
+ }
+
+ // Special case for JS files.
+ if ($tokens[$previousContent]['code'] === T_COMMA
+ || $tokens[$previousContent]['code'] === T_SEMICOLON
+ ) {
+ $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($previousContent - 1), null, true);
+ if ($tokens[$lastContent]['code'] === T_CLOSE_CURLY_BRACKET) {
+ return;
+ }
+ }
+ }
+
+ $comment = rtrim($tokens[$stackPtr]['content']);
+
+ // Only want inline comments.
+ if (substr($comment, 0, 2) !== '//') {
+ return;
+ }
+
+ if (trim(substr($comment, 2)) !== '') {
+ $spaceCount = 0;
+ $tabFound = false;
+
+ $commentLength = strlen($comment);
+ for ($i = 2; $i < $commentLength; $i++) {
+ if ($comment[$i] === "\t") {
+ $tabFound = true;
+ break;
+ }
+
+ if ($comment[$i] !== ' ') {
+ break;
+ }
+
+ $spaceCount++;
+ }
+
+ $fix = false;
+ if ($tabFound === true) {
+ $error = 'Tab found before comment text; expected "// %s" but found "%s"';
+ $data = array(
+ ltrim(substr($comment, 2)),
+ $comment,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'TabBefore', $data);
+ } else if ($spaceCount === 0) {
+ $error = 'No space found before comment text; expected "// %s" but found "%s"';
+ $data = array(
+ substr($comment, 2),
+ $comment,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBefore', $data);
+ } else if ($spaceCount > 1) {
+ $error = 'Expected 1 space before comment text but found %s; use block comment if you need indentation';
+ $data = array(
+ $spaceCount,
+ substr($comment, (2 + $spaceCount)),
+ $comment,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBefore', $data);
+ }//end if
+
+ if ($fix === true) {
+ $newComment = '// '.ltrim($tokens[$stackPtr]['content'], "/\t ");
+ $phpcsFile->fixer->replaceToken($stackPtr, $newComment);
+ }
+ }//end if
+
+ // The below section determines if a comment block is correctly capitalised,
+ // and ends in a full-stop. It will find the last comment in a block, and
+ // work its way up.
+ $nextComment = $phpcsFile->findNext(T_COMMENT, ($stackPtr + 1), null, false);
+ if ($nextComment !== false
+ && $tokens[$nextComment]['line'] === ($tokens[$stackPtr]['line'] + 1)
+ ) {
+ $nextNonWhitespace = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), $nextComment, true);
+ if ($nextNonWhitespace === false) {
+ return;
+ }
+ }
+
+ $topComment = $stackPtr;
+ $lastComment = $stackPtr;
+ while (($topComment = $phpcsFile->findPrevious(array(T_COMMENT), ($lastComment - 1), null, false)) !== false) {
+ if ($tokens[$topComment]['line'] !== ($tokens[$lastComment]['line'] - 1)) {
+ break;
+ }
+
+ $nextNonWhitespace = $phpcsFile->findNext(T_WHITESPACE, ($topComment + 1), $lastComment, true);
+ if ($nextNonWhitespace !== false) {
+ break;
+ }
+
+ $lastComment = $topComment;
+ }
+
+ $topComment = $lastComment;
+ $commentText = '';
+
+ for ($i = $topComment; $i <= $stackPtr; $i++) {
+ if ($tokens[$i]['code'] === T_COMMENT) {
+ $commentText .= trim(substr($tokens[$i]['content'], 2));
+ }
+ }
+
+ if ($commentText === '') {
+ $error = 'Blank comments are not allowed';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Empty');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, '');
+ }
+
+ return;
+ }
+
+ if (preg_match('/^\p{Ll}/u', $commentText) === 1) {
+ $error = 'Inline comments must start with a capital letter';
+ $phpcsFile->addError($error, $topComment, 'NotCapital');
+ }
+
+ // Only check the end of comment character if the start of the comment
+ // is a letter, indicating that the comment is just standard text.
+ if (preg_match('/^\p{L}/u', $commentText) === 1) {
+ $commentCloser = $commentText[(strlen($commentText) - 1)];
+ $acceptedClosers = array(
+ 'full-stops' => '.',
+ 'exclamation marks' => '!',
+ 'or question marks' => '?',
+ );
+
+ if (in_array($commentCloser, $acceptedClosers) === false) {
+ $error = 'Inline comments must end in %s';
+ $ender = '';
+ foreach ($acceptedClosers as $closerName => $symbol) {
+ $ender .= ' '.$closerName.',';
+ }
+
+ $ender = trim($ender, ' ,');
+ $data = array($ender);
+ $phpcsFile->addError($error, $stackPtr, 'InvalidEndChar', $data);
+ }
+ }
+
+ // Finally, the line below the last comment cannot be empty if this inline
+ // comment is on a line by itself.
+ if ($tokens[$previousContent]['line'] < $tokens[$stackPtr]['line']) {
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($next === false) {
+ // Ignore if the comment is the last non-whitespace token in a file.
+ return;
+ }
+
+ $start = false;
+ for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) {
+ if ($tokens[$i]['line'] === ($tokens[$stackPtr]['line'] + 1)) {
+ if ($tokens[$i]['code'] !== T_WHITESPACE) {
+ return;
+ }
+ } else if ($tokens[$i]['line'] > ($tokens[$stackPtr]['line'] + 1)) {
+ break;
+ }
+ }
+
+ $error = 'There must be no blank line following an inline comment';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfter');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($stackPtr + 1); $i < $next; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$next]['line']) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures long conditions have a comment at the end.
+ *
+ * @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\Standards\Squiz\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class LongConditionClosingCommentSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+ /**
+ * The openers that we are interested in.
+ *
+ * @var integer[]
+ */
+ private static $openers = array(
+ T_SWITCH,
+ T_IF,
+ T_FOR,
+ T_FOREACH,
+ T_WHILE,
+ T_TRY,
+ T_CASE,
+ );
+
+ /**
+ * The length that a code block must be before
+ * requiring a closing comment.
+ *
+ * @var integer
+ */
+ public $lineLimit = 20;
+
+ /**
+ * The format the end comment should be in.
+ *
+ * The placeholder %s will be replaced with the type of condition opener.
+ *
+ * @var string
+ */
+ public $commentFormat = '//end %s';
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_CLOSE_CURLY_BRACKET);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[$stackPtr]['scope_condition']) === false) {
+ // No scope condition. It is a function closer.
+ return;
+ }
+
+ $startCondition = $tokens[$tokens[$stackPtr]['scope_condition']];
+ $startBrace = $tokens[$tokens[$stackPtr]['scope_opener']];
+ $endBrace = $tokens[$stackPtr];
+
+ // We are only interested in some code blocks.
+ if (in_array($startCondition['code'], self::$openers) === false) {
+ return;
+ }
+
+ if ($startCondition['code'] === T_IF) {
+ // If this is actually an ELSE IF, skip it as the brace
+ // will be checked by the original IF.
+ $else = $phpcsFile->findPrevious(T_WHITESPACE, ($tokens[$stackPtr]['scope_condition'] - 1), null, true);
+ if ($tokens[$else]['code'] === T_ELSE) {
+ return;
+ }
+
+ // IF statements that have an ELSE block need to use
+ // "end if" rather than "end else" or "end elseif".
+ do {
+ $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$nextToken]['code'] === T_ELSE || $tokens[$nextToken]['code'] === T_ELSEIF) {
+ // Check for ELSE IF (2 tokens) as opposed to ELSEIF (1 token).
+ if ($tokens[$nextToken]['code'] === T_ELSE
+ && isset($tokens[$nextToken]['scope_closer']) === false
+ ) {
+ $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($nextToken + 1), null, true);
+ if ($tokens[$nextToken]['code'] !== T_IF
+ || isset($tokens[$nextToken]['scope_closer']) === false
+ ) {
+ // Not an ELSE IF or is an inline ELSE IF.
+ break;
+ }
+ }
+
+ if (isset($tokens[$nextToken]['scope_closer']) === false) {
+ // There isn't going to be anywhere to print the "end if" comment
+ // because there is no closer.
+ return;
+ }
+
+ // The end brace becomes the ELSE's end brace.
+ $stackPtr = $tokens[$nextToken]['scope_closer'];
+ $endBrace = $tokens[$stackPtr];
+ } else {
+ break;
+ }//end if
+ } while (isset($tokens[$nextToken]['scope_closer']) === true);
+ }//end if
+
+ if ($startCondition['code'] === T_TRY) {
+ // TRY statements need to check until the end of all CATCH statements.
+ do {
+ $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$nextToken]['code'] === T_CATCH
+ || $tokens[$nextToken]['code'] === T_FINALLY
+ ) {
+ // The end brace becomes the CATCH's end brace.
+ $stackPtr = $tokens[$nextToken]['scope_closer'];
+ $endBrace = $tokens[$stackPtr];
+ } else {
+ break;
+ }
+ } while (isset($tokens[$nextToken]['scope_closer']) === true);
+ }
+
+ $lineDifference = ($endBrace['line'] - $startBrace['line']);
+
+ $expected = sprintf($this->commentFormat, $startCondition['content']);
+ $comment = $phpcsFile->findNext(array(T_COMMENT), $stackPtr, null, false);
+
+ if (($comment === false) || ($tokens[$comment]['line'] !== $endBrace['line'])) {
+ if ($lineDifference >= $this->lineLimit) {
+ $error = 'End comment for long condition not found; expected "%s"';
+ $data = array($expected);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Missing', $data);
+
+ if ($fix === true) {
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($next !== false && $tokens[$next]['line'] === $tokens[$stackPtr]['line']) {
+ $expected .= $phpcsFile->eolChar;
+ }
+
+ $phpcsFile->fixer->addContent($stackPtr, $expected);
+ }
+ }
+
+ return;
+ }
+
+ if (($comment - $stackPtr) !== 1) {
+ $error = 'Space found before closing comment; expected "%s"';
+ $data = array($expected);
+ $phpcsFile->addError($error, $stackPtr, 'SpacingBefore', $data);
+ }
+
+ if (trim($tokens[$comment]['content']) !== $expected) {
+ $found = trim($tokens[$comment]['content']);
+ $error = 'Incorrect closing comment; expected "%s" but found "%s"';
+ $data = array(
+ $expected,
+ $found,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Invalid', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($comment, $expected.$phpcsFile->eolChar);
+ }
+
+ return;
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks to ensure that there are no comments after statements.
+ *
+ * @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\Standards\Squiz\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class PostStatementCommentSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_COMMENT);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (substr($tokens[$stackPtr]['content'], 0, 2) !== '//') {
+ return;
+ }
+
+ $commentLine = $tokens[$stackPtr]['line'];
+ $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+
+ if ($tokens[$lastContent]['line'] !== $commentLine) {
+ return;
+ }
+
+ if ($tokens[$lastContent]['code'] === T_CLOSE_CURLY_BRACKET) {
+ return;
+ }
+
+ // Special case for JS files.
+ if ($tokens[$lastContent]['code'] === T_COMMA
+ || $tokens[$lastContent]['code'] === T_SEMICOLON
+ ) {
+ $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($lastContent - 1), null, true);
+ if ($tokens[$lastContent]['code'] === T_CLOSE_CURLY_BRACKET) {
+ return;
+ }
+ }
+
+ $error = 'Comments may not appear after statements';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewlineBefore($stackPtr);
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Parses and verifies the variable doc comment.
+ *
+ * @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\Standards\Squiz\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Common;
+
+class VariableCommentSniff extends AbstractVariableSniff
+{
+
+
+ /**
+ * Called to process class member vars.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function processMemberVar(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $ignore = array(
+ T_PUBLIC,
+ T_PRIVATE,
+ T_PROTECTED,
+ T_VAR,
+ T_STATIC,
+ T_WHITESPACE,
+ );
+
+ $commentEnd = $phpcsFile->findPrevious($ignore, ($stackPtr - 1), null, true);
+ if ($commentEnd === false
+ || ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
+ && $tokens[$commentEnd]['code'] !== T_COMMENT)
+ ) {
+ $phpcsFile->addError('Missing member variable doc comment', $stackPtr, 'Missing');
+ return;
+ }
+
+ if ($tokens[$commentEnd]['code'] === T_COMMENT) {
+ $phpcsFile->addError('You must use "/**" style comments for a member variable comment', $stackPtr, 'WrongStyle');
+ return;
+ }
+
+ $commentStart = $tokens[$commentEnd]['comment_opener'];
+
+ $foundVar = null;
+ foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
+ if ($tokens[$tag]['content'] === '@var') {
+ if ($foundVar !== null) {
+ $error = 'Only one @var tag is allowed in a member variable comment';
+ $phpcsFile->addError($error, $tag, 'DuplicateVar');
+ } else {
+ $foundVar = $tag;
+ }
+ } else if ($tokens[$tag]['content'] === '@see') {
+ // Make sure the tag isn't empty.
+ $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd);
+ if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) {
+ $error = 'Content missing for @see tag in member variable comment';
+ $phpcsFile->addError($error, $tag, 'EmptySees');
+ }
+ } else {
+ $error = '%s tag is not allowed in member variable comment';
+ $data = array($tokens[$tag]['content']);
+ $phpcsFile->addWarning($error, $tag, 'TagNotAllowed', $data);
+ }//end if
+ }//end foreach
+
+ // The @var tag is the only one we require.
+ if ($foundVar === null) {
+ $error = 'Missing @var tag in member variable comment';
+ $phpcsFile->addError($error, $commentEnd, 'MissingVar');
+ return;
+ }
+
+ $firstTag = $tokens[$commentStart]['comment_tags'][0];
+ if ($foundVar !== null && $tokens[$firstTag]['content'] !== '@var') {
+ $error = 'The @var tag must be the first tag in a member variable comment';
+ $phpcsFile->addError($error, $foundVar, 'VarOrder');
+ }
+
+ // Make sure the tag isn't empty and has the correct padding.
+ $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $foundVar, $commentEnd);
+ if ($string === false || $tokens[$string]['line'] !== $tokens[$foundVar]['line']) {
+ $error = 'Content missing for @var tag in member variable comment';
+ $phpcsFile->addError($error, $foundVar, 'EmptyVar');
+ return;
+ }
+
+ $varType = $tokens[($foundVar + 2)]['content'];
+ $suggestedType = Common::suggestType($varType);
+ if ($varType !== $suggestedType) {
+ $error = 'Expected "%s" but found "%s" for @var tag in member variable comment';
+ $data = array(
+ $suggestedType,
+ $varType,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, ($foundVar + 2), 'IncorrectVarType', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($foundVar + 2), $suggestedType);
+ }
+ }
+
+ }//end processMemberVar()
+
+
+ /**
+ * Called to process a normal variable.
+ *
+ * Not required for this sniff.
+ *
+ * @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
+ */
+ protected function processVariable(File $phpcsFile, $stackPtr)
+ {
+
+ }//end processVariable()
+
+
+ /**
+ * Called to process variables found in double quoted strings.
+ *
+ * Not required for this sniff.
+ *
+ * @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
+ */
+ protected function processVariableInString(File $phpcsFile, $stackPtr)
+ {
+
+ }//end processVariableInString()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that control statements conform to their coding standards.
+ *
+ * @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\Standards\Squiz\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ControlSignatureSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(
+ T_TRY,
+ T_CATCH,
+ T_DO,
+ T_WHILE,
+ T_FOR,
+ T_IF,
+ T_FOREACH,
+ T_ELSE,
+ T_ELSEIF,
+ T_SWITCH,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[($stackPtr + 1)]) === false) {
+ return;
+ }
+
+ // Single space after the keyword.
+ $found = 1;
+ if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
+ $found = 0;
+ } else if ($tokens[($stackPtr + 1)]['content'] !== ' ') {
+ if (strpos($tokens[($stackPtr + 1)]['content'], $phpcsFile->eolChar) !== false) {
+ $found = 'newline';
+ } else {
+ $found = strlen($tokens[($stackPtr + 1)]['content']);
+ }
+ }
+
+ if ($found !== 1) {
+ $error = 'Expected 1 space after %s keyword; %s found';
+ $data = array(
+ strtoupper($tokens[$stackPtr]['content']),
+ $found,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterKeyword', $data);
+ if ($fix === true) {
+ if ($found === 0) {
+ $phpcsFile->fixer->addContent($stackPtr, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+ }
+
+ // Single space after closing parenthesis.
+ if (isset($tokens[$stackPtr]['parenthesis_closer']) === true
+ && isset($tokens[$stackPtr]['scope_opener']) === true
+ ) {
+ $closer = $tokens[$stackPtr]['parenthesis_closer'];
+ $opener = $tokens[$stackPtr]['scope_opener'];
+ $content = $phpcsFile->getTokensAsString(($closer + 1), ($opener - $closer - 1));
+
+ if ($content !== ' ') {
+ $error = 'Expected 1 space after closing parenthesis; found %s';
+ if ($tokens[$closer]['line'] !== $tokens[$opener]['line']) {
+ $found = 'newline';
+ } else if (trim($content) === '') {
+ $found = strlen($content);
+ } else {
+ $found = '"'.str_replace($phpcsFile->eolChar, '\n', $content).'"';
+ }
+
+ $fix = $phpcsFile->addFixableError($error, $closer, 'SpaceAfterCloseParenthesis', array($found));
+ if ($fix === true) {
+ if ($closer === ($opener - 1)) {
+ $phpcsFile->fixer->addContent($closer, ' ');
+ } else {
+ $phpcsFile->fixer->beginChangeset();
+ if (trim($content) === '') {
+ $phpcsFile->fixer->addContent($closer, ' ');
+ if ($found !== 0) {
+ for ($i = ($closer + 1); $i < $opener; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+ }
+ } else {
+ $phpcsFile->fixer->addContent($closer, ' '.$tokens[$opener]['content']);
+ $phpcsFile->fixer->replaceToken($opener, '');
+
+ if ($tokens[$opener]['line'] !== $tokens[$closer]['line']) {
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($opener + 1), null, true);
+ if ($tokens[$next]['line'] !== $tokens[$opener]['line']) {
+ for ($i = ($opener + 1); $i < $next; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+ }
+ }
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+ }//end if
+ }//end if
+ }//end if
+
+ // Single newline after opening brace.
+ if (isset($tokens[$stackPtr]['scope_opener']) === true) {
+ $opener = $tokens[$stackPtr]['scope_opener'];
+ for ($next = ($opener + 1); $next < $phpcsFile->numTokens; $next++) {
+ $code = $tokens[$next]['code'];
+
+ if ($code === T_WHITESPACE
+ || ($code === T_INLINE_HTML
+ && trim($tokens[$next]['content']) === '')
+ ) {
+ continue;
+ }
+
+ // Skip all empty tokens on the same line as the opener.
+ if ($tokens[$next]['line'] === $tokens[$opener]['line']
+ && (isset(Tokens::$emptyTokens[$code]) === true
+ || $code === T_CLOSE_TAG)
+ ) {
+ continue;
+ }
+
+ // We found the first bit of a code, or a comment on the
+ // following line.
+ break;
+ }//end for
+
+ if ($tokens[$next]['line'] === $tokens[$opener]['line']) {
+ $error = 'Newline required after opening brace';
+ $fix = $phpcsFile->addFixableError($error, $opener, 'NewlineAfterOpenBrace');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($opener + 1); $i < $next; $i++) {
+ if (trim($tokens[$i]['content']) !== '') {
+ break;
+ }
+
+ // Remove whitespace.
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->addContent($opener, $phpcsFile->eolChar);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }//end if
+ } else if ($tokens[$stackPtr]['code'] === T_WHILE) {
+ // Zero spaces after parenthesis closer.
+ $closer = $tokens[$stackPtr]['parenthesis_closer'];
+ $found = 0;
+ if ($tokens[($closer + 1)]['code'] === T_WHITESPACE) {
+ if (strpos($tokens[($closer + 1)]['content'], $phpcsFile->eolChar) !== false) {
+ $found = 'newline';
+ } else {
+ $found = strlen($tokens[($closer + 1)]['content']);
+ }
+ }
+
+ if ($found !== 0) {
+ $error = 'Expected 0 spaces before semicolon; %s found';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $closer, 'SpaceBeforeSemicolon', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($closer + 1), '');
+ }
+ }
+ }//end if
+
+ // Only want to check multi-keyword structures from here on.
+ if ($tokens[$stackPtr]['code'] === T_DO) {
+ if (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ return;
+ }
+
+ $closer = $tokens[$stackPtr]['scope_closer'];
+ } else if ($tokens[$stackPtr]['code'] === T_ELSE
+ || $tokens[$stackPtr]['code'] === T_ELSEIF
+ || $tokens[$stackPtr]['code'] === T_CATCH
+ ) {
+ if (isset($tokens[$stackPtr]['scope_opener']) === true
+ && $tokens[$tokens[$stackPtr]['scope_opener']]['code'] === T_COLON
+ ) {
+ // Special case for alternate syntax, where this token is actually
+ // the closer for the previous block, so there is no spacing to check.
+ return;
+ }
+
+ $closer = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($closer === false || $tokens[$closer]['code'] !== T_CLOSE_CURLY_BRACKET) {
+ return;
+ }
+ } else {
+ return;
+ }//end if
+
+ // Single space after closing brace.
+ $found = 1;
+ if ($tokens[($closer + 1)]['code'] !== T_WHITESPACE) {
+ $found = 0;
+ } else if ($tokens[($closer + 1)]['content'] !== ' ') {
+ if (strpos($tokens[($closer + 1)]['content'], $phpcsFile->eolChar) !== false) {
+ $found = 'newline';
+ } else {
+ $found = strlen($tokens[($closer + 1)]['content']);
+ }
+ }
+
+ if ($found !== 1) {
+ $error = 'Expected 1 space after closing brace; %s found';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $closer, 'SpaceAfterCloseBrace', $data);
+ if ($fix === true) {
+ if ($found === 0) {
+ $phpcsFile->fixer->addContent($closer, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($closer + 1), ' ');
+ }
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures the use of else if over elseif.
+ *
+ * @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\Standards\Squiz\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ElseIfDeclarationSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_ELSEIF);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $error = 'Usage of ELSEIF not allowed; use ELSE IF instead';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotAllowed');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, 'else if');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that there is a space between each condition of foreach loops.
+ *
+ * @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\Standards\Squiz\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ForEachLoopDeclarationSniff implements Sniff
+{
+
+
+ /**
+ * How many spaces should follow the opening bracket.
+ *
+ * @var integer
+ */
+ public $requiredSpacesAfterOpen = 0;
+
+ /**
+ * How many spaces should precede the closing bracket.
+ *
+ * @var integer
+ */
+ public $requiredSpacesBeforeClose = 0;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_FOREACH);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $this->requiredSpacesAfterOpen = (int) $this->requiredSpacesAfterOpen;
+ $this->requiredSpacesBeforeClose = (int) $this->requiredSpacesBeforeClose;
+ $tokens = $phpcsFile->getTokens();
+
+ $openingBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr);
+ if ($openingBracket === false) {
+ $error = 'Possible parse error: FOREACH has no opening parenthesis';
+ $phpcsFile->addWarning($error, $stackPtr, 'MissingOpenParenthesis');
+ return;
+ }
+
+ if (isset($tokens[$openingBracket]['parenthesis_closer']) === false) {
+ $error = 'Possible parse error: FOREACH has no closing parenthesis';
+ $phpcsFile->addWarning($error, $stackPtr, 'MissingCloseParenthesis');
+ return;
+ }
+
+ $closingBracket = $tokens[$openingBracket]['parenthesis_closer'];
+
+ if ($this->requiredSpacesAfterOpen === 0 && $tokens[($openingBracket + 1)]['code'] === T_WHITESPACE) {
+ $error = 'Space found after opening bracket of FOREACH loop';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterOpen');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($openingBracket + 1), '');
+ }
+ } else if ($this->requiredSpacesAfterOpen > 0) {
+ $spaceAfterOpen = 0;
+ if ($tokens[($openingBracket + 1)]['code'] === T_WHITESPACE) {
+ $spaceAfterOpen = strlen($tokens[($openingBracket + 1)]['content']);
+ }
+
+ if ($spaceAfterOpen !== $this->requiredSpacesAfterOpen) {
+ $error = 'Expected %s spaces after opening bracket; %s found';
+ $data = array(
+ $this->requiredSpacesAfterOpen,
+ $spaceAfterOpen,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterOpen', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $this->requiredSpacesAfterOpen);
+ if ($spaceAfterOpen === 0) {
+ $phpcsFile->fixer->addContent($openingBracket, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($openingBracket + 1), $padding);
+ }
+ }
+ }
+ }//end if
+
+ if ($this->requiredSpacesBeforeClose === 0 && $tokens[($closingBracket - 1)]['code'] === T_WHITESPACE) {
+ $error = 'Space found before closing bracket of FOREACH loop';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceBeforeClose');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($closingBracket - 1), '');
+ }
+ } else if ($this->requiredSpacesBeforeClose > 0) {
+ $spaceBeforeClose = 0;
+ if ($tokens[($closingBracket - 1)]['code'] === T_WHITESPACE) {
+ $spaceBeforeClose = strlen($tokens[($closingBracket - 1)]['content']);
+ }
+
+ if ($spaceBeforeClose !== $this->requiredSpacesBeforeClose) {
+ $error = 'Expected %s spaces before closing bracket; %s found';
+ $data = array(
+ $this->requiredSpacesBeforeClose,
+ $spaceBeforeClose,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceBeforeClose', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $this->requiredSpacesBeforeClose);
+ if ($spaceBeforeClose === 0) {
+ $phpcsFile->fixer->addContentBefore($closingBracket, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($closingBracket - 1), $padding);
+ }
+ }
+ }
+ }//end if
+
+ $asToken = $phpcsFile->findNext(T_AS, $openingBracket);
+ if ($asToken === false) {
+ $error = 'Possible parse error: FOREACH has no AS statement';
+ $phpcsFile->addWarning($error, $stackPtr, 'MissingAs');
+ return;
+ }
+
+ $content = $tokens[$asToken]['content'];
+ if ($content !== strtolower($content)) {
+ $expected = strtolower($content);
+ $error = 'AS keyword must be lowercase; expected "%s" but found "%s"';
+ $data = array(
+ $expected,
+ $content,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $asToken, 'AsNotLower', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($asToken, $expected);
+ }
+ }
+
+ $doubleArrow = $phpcsFile->findNext(T_DOUBLE_ARROW, $asToken, $closingBracket);
+
+ if ($doubleArrow !== false) {
+ if ($tokens[($doubleArrow - 1)]['code'] !== T_WHITESPACE) {
+ $error = 'Expected 1 space before "=>"; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBeforeArrow');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContentBefore($doubleArrow, ' ');
+ }
+ } else {
+ if (strlen($tokens[($doubleArrow - 1)]['content']) !== 1) {
+ $spaces = strlen($tokens[($doubleArrow - 1)]['content']);
+ $error = 'Expected 1 space before "=>"; %s found';
+ $data = array($spaces);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeArrow', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($doubleArrow - 1), ' ');
+ }
+ }
+ }
+
+ if ($tokens[($doubleArrow + 1)]['code'] !== T_WHITESPACE) {
+ $error = 'Expected 1 space after "=>"; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfterArrow');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($doubleArrow, ' ');
+ }
+ } else {
+ if (strlen($tokens[($doubleArrow + 1)]['content']) !== 1) {
+ $spaces = strlen($tokens[($doubleArrow + 1)]['content']);
+ $error = 'Expected 1 space after "=>"; %s found';
+ $data = array($spaces);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterArrow', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($doubleArrow + 1), ' ');
+ }
+ }
+ }
+ }//end if
+
+ if ($tokens[($asToken - 1)]['code'] !== T_WHITESPACE) {
+ $error = 'Expected 1 space before "as"; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBeforeAs');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContentBefore($asToken, ' ');
+ }
+ } else {
+ if (strlen($tokens[($asToken - 1)]['content']) !== 1) {
+ $spaces = strlen($tokens[($asToken - 1)]['content']);
+ $error = 'Expected 1 space before "as"; %s found';
+ $data = array($spaces);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeAs', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($asToken - 1), ' ');
+ }
+ }
+ }
+
+ if ($tokens[($asToken + 1)]['code'] !== T_WHITESPACE) {
+ $error = 'Expected 1 space after "as"; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfterAs');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($asToken, ' ');
+ }
+ } else {
+ if (strlen($tokens[($asToken + 1)]['content']) !== 1) {
+ $spaces = strlen($tokens[($asToken + 1)]['content']);
+ $error = 'Expected 1 space after "as"; %s found';
+ $data = array($spaces);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterAs', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($asToken + 1), ' ');
+ }
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that there is a space between each condition of for loops.
+ *
+ * @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\Standards\Squiz\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ForLoopDeclarationSniff implements Sniff
+{
+
+ /**
+ * How many spaces should follow the opening bracket.
+ *
+ * @var integer
+ */
+ public $requiredSpacesAfterOpen = 0;
+
+ /**
+ * How many spaces should precede the closing bracket.
+ *
+ * @var integer
+ */
+ public $requiredSpacesBeforeClose = 0;
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_FOR);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $this->requiredSpacesAfterOpen = (int) $this->requiredSpacesAfterOpen;
+ $this->requiredSpacesBeforeClose = (int) $this->requiredSpacesBeforeClose;
+ $tokens = $phpcsFile->getTokens();
+
+ $openingBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr);
+ if ($openingBracket === false) {
+ $error = 'Possible parse error: no opening parenthesis for FOR keyword';
+ $phpcsFile->addWarning($error, $stackPtr, 'NoOpenBracket');
+ return;
+ }
+
+ $closingBracket = $tokens[$openingBracket]['parenthesis_closer'];
+
+ if ($this->requiredSpacesAfterOpen === 0 && $tokens[($openingBracket + 1)]['code'] === T_WHITESPACE) {
+ $error = 'Space found after opening bracket of FOR loop';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterOpen');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($openingBracket + 1), '');
+ }
+ } else if ($this->requiredSpacesAfterOpen > 0) {
+ $spaceAfterOpen = 0;
+ if ($tokens[($openingBracket + 1)]['code'] === T_WHITESPACE) {
+ $spaceAfterOpen = strlen($tokens[($openingBracket + 1)]['content']);
+ }
+
+ if ($spaceAfterOpen !== $this->requiredSpacesAfterOpen) {
+ $error = 'Expected %s spaces after opening bracket; %s found';
+ $data = array(
+ $this->requiredSpacesAfterOpen,
+ $spaceAfterOpen,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterOpen', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $this->requiredSpacesAfterOpen);
+ if ($spaceAfterOpen === 0) {
+ $phpcsFile->fixer->addContent($openingBracket, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($openingBracket + 1), $padding);
+ }
+ }
+ }
+ }//end if
+
+ if ($this->requiredSpacesBeforeClose === 0 && $tokens[($closingBracket - 1)]['code'] === T_WHITESPACE) {
+ $error = 'Space found before closing bracket of FOR loop';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeClose');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($closingBracket - 1), '');
+ }
+ } else if ($this->requiredSpacesBeforeClose > 0) {
+ $spaceBeforeClose = 0;
+ if ($tokens[($closingBracket - 1)]['code'] === T_WHITESPACE) {
+ $spaceBeforeClose = strlen($tokens[($closingBracket - 1)]['content']);
+ }
+
+ if ($this->requiredSpacesBeforeClose !== $spaceBeforeClose) {
+ $error = 'Expected %s spaces before closing bracket; %s found';
+ $data = array(
+ $this->requiredSpacesBeforeClose,
+ $spaceBeforeClose,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeClose', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $this->requiredSpacesBeforeClose);
+ if ($spaceBeforeClose === 0) {
+ $phpcsFile->fixer->addContentBefore($closingBracket, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($closingBracket - 1), $padding);
+ }
+ }
+ }
+ }//end if
+
+ $firstSemicolon = $phpcsFile->findNext(T_SEMICOLON, $openingBracket, $closingBracket);
+
+ // Check whitespace around each of the tokens.
+ if ($firstSemicolon !== false) {
+ if ($tokens[($firstSemicolon - 1)]['code'] === T_WHITESPACE) {
+ $error = 'Space found before first semicolon of FOR loop';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeFirst');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($firstSemicolon - 1), '');
+ }
+ }
+
+ if ($tokens[($firstSemicolon + 1)]['code'] !== T_WHITESPACE
+ && $tokens[($firstSemicolon + 1)]['code'] !== T_SEMICOLON
+ ) {
+ $error = 'Expected 1 space after first semicolon of FOR loop; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfterFirst');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($firstSemicolon, ' ');
+ }
+ } else {
+ if (strlen($tokens[($firstSemicolon + 1)]['content']) !== 1) {
+ $spaces = strlen($tokens[($firstSemicolon + 1)]['content']);
+ $error = 'Expected 1 space after first semicolon of FOR loop; %s found';
+ $data = array($spaces);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterFirst', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($firstSemicolon + 1), ' ');
+ }
+ }
+ }
+
+ $secondSemicolon = $phpcsFile->findNext(T_SEMICOLON, ($firstSemicolon + 1));
+
+ if ($secondSemicolon !== false) {
+ if ($tokens[($secondSemicolon - 1)]['code'] === T_WHITESPACE
+ && $tokens[($firstSemicolon + 1)]['code'] !== T_SEMICOLON
+ ) {
+ $error = 'Space found before second semicolon of FOR loop';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeSecond');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($secondSemicolon - 1), '');
+ }
+ }
+
+ if (($secondSemicolon + 1) !== $closingBracket
+ && $tokens[($secondSemicolon + 1)]['code'] !== T_WHITESPACE
+ ) {
+ $error = 'Expected 1 space after second semicolon of FOR loop; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfterSecond');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($secondSemicolon, ' ');
+ }
+ } else {
+ if (strlen($tokens[($secondSemicolon + 1)]['content']) !== 1) {
+ $spaces = strlen($tokens[($secondSemicolon + 1)]['content']);
+ $data = array($spaces);
+ if (($secondSemicolon + 2) === $closingBracket) {
+ $error = 'Expected no space after second semicolon of FOR loop; %s found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterSecondNoThird', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($secondSemicolon + 1), '');
+ }
+ } else {
+ $error = 'Expected 1 space after second semicolon of FOR loop; %s found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterSecond', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($secondSemicolon + 1), ' ');
+ }
+ }
+ }
+ }//end if
+ }//end if
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tests the spacing of shorthand IF statements.
+ *
+ * @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\Standards\Squiz\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class InlineIfDeclarationSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_INLINE_THEN);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $openBracket = null;
+ $closeBracket = null;
+ if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
+ $parens = $tokens[$stackPtr]['nested_parenthesis'];
+ $openBracket = array_pop($parens);
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+ }
+
+ // Find the beginning of the statement. If we don't find a
+ // semicolon (end of statement) or comma (end of array value)
+ // then assume the content before the closing parenthesis is the end.
+ $else = $phpcsFile->findNext(T_INLINE_ELSE, ($stackPtr + 1));
+ $statementEnd = $phpcsFile->findNext(array(T_SEMICOLON, T_COMMA), ($else + 1), $closeBracket);
+ if ($statementEnd === false) {
+ $statementEnd = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBracket - 1), null, true);
+ }
+
+ // Make sure it's all on the same line.
+ if ($tokens[$statementEnd]['line'] !== $tokens[$stackPtr]['line']) {
+ $error = 'Inline shorthand IF statement must be declared on a single line';
+ $phpcsFile->addError($error, $stackPtr, 'NotSingleLine');
+ return;
+ }
+
+ // Make sure there are spaces around the question mark.
+ $contentBefore = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ $contentAfter = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$contentBefore]['code'] !== T_CLOSE_PARENTHESIS) {
+ $error = 'Inline shorthand IF statement requires brackets around comparison';
+ $phpcsFile->addError($error, $stackPtr, 'NoBrackets');
+ }
+
+ $spaceBefore = ($tokens[$stackPtr]['column'] - ($tokens[$contentBefore]['column'] + $tokens[$contentBefore]['length']));
+ if ($spaceBefore !== 1) {
+ $error = 'Inline shorthand IF statement requires 1 space before THEN; %s found';
+ $data = array($spaceBefore);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeThen', $data);
+ if ($fix === true) {
+ if ($spaceBefore === 0) {
+ $phpcsFile->fixer->addContentBefore($stackPtr, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
+ }
+ }
+ }
+
+ // If there is no content between the ? and the : operators, then they are
+ // trying to replicate an elvis operator, even though PHP doesn't have one.
+ // In this case, we want no spaces between the two operators so ?: looks like
+ // an operator itself.
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$next]['code'] === T_INLINE_ELSE) {
+ $inlineElse = $next;
+ if ($inlineElse !== ($stackPtr + 1)) {
+ $error = 'Inline shorthand IF statement without THEN statement requires 0 spaces between THEN and ELSE';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'ElvisSpacing');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), '');
+ }
+ }
+ } else {
+ $spaceAfter = (($tokens[$contentAfter]['column']) - ($tokens[$stackPtr]['column'] + 1));
+ if ($spaceAfter !== 1) {
+ $error = 'Inline shorthand IF statement requires 1 space after THEN; %s found';
+ $data = array($spaceAfter);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterThen', $data);
+ if ($fix === true) {
+ if ($spaceAfter === 0) {
+ $phpcsFile->fixer->addContent($stackPtr, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+ }
+
+ // Make sure the ELSE has the correct spacing.
+ $inlineElse = $phpcsFile->findNext(T_INLINE_ELSE, ($stackPtr + 1), $statementEnd, false);
+ $contentBefore = $phpcsFile->findPrevious(T_WHITESPACE, ($inlineElse - 1), null, true);
+ $spaceBefore = ($tokens[$inlineElse]['column'] - ($tokens[$contentBefore]['column'] + $tokens[$contentBefore]['length']));
+ if ($spaceBefore !== 1) {
+ $error = 'Inline shorthand IF statement requires 1 space before ELSE; %s found';
+ $data = array($spaceBefore);
+ $fix = $phpcsFile->addFixableError($error, $inlineElse, 'SpacingBeforeElse', $data);
+ if ($fix === true) {
+ if ($spaceBefore === 0) {
+ $phpcsFile->fixer->addContentBefore($inlineElse, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($inlineElse - 1), ' ');
+ }
+ }
+ }
+ }//end if
+
+ $contentAfter = $phpcsFile->findNext(T_WHITESPACE, ($inlineElse + 1), null, true);
+ $spaceAfter = (($tokens[$contentAfter]['column']) - ($tokens[$inlineElse]['column'] + 1));
+ if ($spaceAfter !== 1) {
+ $error = 'Inline shorthand IF statement requires 1 space after ELSE; %s found';
+ $data = array($spaceAfter);
+ $fix = $phpcsFile->addFixableError($error, $inlineElse, 'SpacingAfterElse', $data);
+ if ($fix === true) {
+ if ($spaceAfter === 0) {
+ $phpcsFile->fixer->addContent($inlineElse, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($inlineElse + 1), ' ');
+ }
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures all control structure keywords are lowercase.
+ *
+ * @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\Standards\Squiz\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class LowercaseDeclarationSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_IF,
+ T_ELSE,
+ T_ELSEIF,
+ T_FOREACH,
+ T_FOR,
+ T_DO,
+ T_SWITCH,
+ T_WHILE,
+ T_TRY,
+ T_CATCH,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $content = $tokens[$stackPtr]['content'];
+ if ($content !== strtolower($content)) {
+ $error = '%s keyword must be lowercase; expected "%s" but found "%s"';
+ $data = array(
+ strtoupper($content),
+ strtolower($content),
+ $content,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'FoundUppercase', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, strtolower($content));
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Enforces switch statement formatting.
+ *
+ * @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\Standards\Squiz\Sniffs\ControlStructures;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class SwitchDeclarationSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+ /**
+ * The number of spaces code should be indented.
+ *
+ * @var integer
+ */
+ public $indent = 4;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_SWITCH);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // We can't process SWITCH statements unless we know where they start and end.
+ if (isset($tokens[$stackPtr]['scope_opener']) === false
+ || isset($tokens[$stackPtr]['scope_closer']) === false
+ ) {
+ return;
+ }
+
+ $switch = $tokens[$stackPtr];
+ $nextCase = $stackPtr;
+ $caseAlignment = ($switch['column'] + $this->indent);
+ $caseCount = 0;
+ $foundDefault = false;
+
+ while (($nextCase = $phpcsFile->findNext(array(T_CASE, T_DEFAULT, T_SWITCH), ($nextCase + 1), $switch['scope_closer'])) !== false) {
+ // Skip nested SWITCH statements; they are handled on their own.
+ if ($tokens[$nextCase]['code'] === T_SWITCH) {
+ $nextCase = $tokens[$nextCase]['scope_closer'];
+ continue;
+ }
+
+ if ($tokens[$nextCase]['code'] === T_DEFAULT) {
+ $type = 'Default';
+ $foundDefault = true;
+ } else {
+ $type = 'Case';
+ $caseCount++;
+ }
+
+ if ($tokens[$nextCase]['content'] !== strtolower($tokens[$nextCase]['content'])) {
+ $expected = strtolower($tokens[$nextCase]['content']);
+ $error = strtoupper($type).' keyword must be lowercase; expected "%s" but found "%s"';
+ $data = array(
+ $expected,
+ $tokens[$nextCase]['content'],
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $nextCase, $type.'NotLower', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($nextCase, $expected);
+ }
+ }
+
+ if ($tokens[$nextCase]['column'] !== $caseAlignment) {
+ $error = strtoupper($type).' keyword must be indented '.$this->indent.' spaces from SWITCH keyword';
+ $fix = $phpcsFile->addFixableError($error, $nextCase, $type.'Indent');
+
+ if ($fix === true) {
+ $padding = str_repeat(' ', ($caseAlignment - 1));
+ if ($tokens[$nextCase]['column'] === 1
+ || $tokens[($nextCase - 1)]['code'] !== T_WHITESPACE
+ ) {
+ $phpcsFile->fixer->addContentBefore($nextCase, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($nextCase - 1), $padding);
+ }
+ }
+ }
+
+ if ($type === 'Case'
+ && ($tokens[($nextCase + 1)]['type'] !== 'T_WHITESPACE'
+ || $tokens[($nextCase + 1)]['content'] !== ' ')
+ ) {
+ $error = 'CASE keyword must be followed by a single space';
+ $fix = $phpcsFile->addFixableError($error, $nextCase, 'SpacingAfterCase');
+ if ($fix === true) {
+ if ($tokens[($nextCase + 1)]['type'] !== 'T_WHITESPACE') {
+ $phpcsFile->fixer->addContent($nextCase, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($nextCase + 1), ' ');
+ }
+ }
+ }
+
+ if (isset($tokens[$nextCase]['scope_opener']) === false) {
+ $error = 'Possible parse error: CASE missing opening colon';
+ $phpcsFile->addWarning($error, $nextCase, 'MissingColon');
+ continue;
+ }
+
+ $opener = $tokens[$nextCase]['scope_opener'];
+ if ($tokens[($opener - 1)]['type'] === 'T_WHITESPACE') {
+ $error = 'There must be no space before the colon in a '.strtoupper($type).' statement';
+ $fix = $phpcsFile->addFixableError($error, $nextCase, 'SpaceBeforeColon'.$type);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($opener - 1), '');
+ }
+ }
+
+ $nextBreak = $tokens[$nextCase]['scope_closer'];
+ if ($tokens[$nextBreak]['code'] === T_BREAK
+ || $tokens[$nextBreak]['code'] === T_RETURN
+ || $tokens[$nextBreak]['code'] === T_CONTINUE
+ || $tokens[$nextBreak]['code'] === T_THROW
+ || $tokens[$nextBreak]['code'] === T_EXIT
+ ) {
+ if ($tokens[$nextBreak]['scope_condition'] === $nextCase) {
+ // Only need to check a couple of things once, even if the
+ // break is shared between multiple case statements, or even
+ // the default case.
+ if ($tokens[$nextBreak]['column'] !== $caseAlignment) {
+ $error = 'Case breaking statement must be indented '.$this->indent.' spaces from SWITCH keyword';
+ $fix = $phpcsFile->addFixableError($error, $nextBreak, 'BreakIndent');
+
+ if ($fix === true) {
+ $padding = str_repeat(' ', ($caseAlignment - 1));
+ if ($tokens[$nextBreak]['column'] === 1
+ || $tokens[($nextBreak - 1)]['code'] !== T_WHITESPACE
+ ) {
+ $phpcsFile->fixer->addContentBefore($nextBreak, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($nextBreak - 1), $padding);
+ }
+ }
+ }
+
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($nextBreak - 1), $stackPtr, true);
+ if ($tokens[$prev]['line'] !== ($tokens[$nextBreak]['line'] - 1)) {
+ $error = 'Blank lines are not allowed before case breaking statements';
+ $phpcsFile->addError($error, $nextBreak, 'SpacingBeforeBreak');
+ }
+
+ $nextLine = $tokens[$tokens[$stackPtr]['scope_closer']]['line'];
+ $semicolon = $phpcsFile->findEndOfStatement($nextBreak);
+ for ($i = ($semicolon + 1); $i < $tokens[$stackPtr]['scope_closer']; $i++) {
+ if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
+ $nextLine = $tokens[$i]['line'];
+ break;
+ }
+ }
+
+ if ($type === 'Case') {
+ // Ensure the BREAK statement is followed by
+ // a single blank line, or the end switch brace.
+ if ($nextLine !== ($tokens[$semicolon]['line'] + 2) && $i !== $tokens[$stackPtr]['scope_closer']) {
+ $error = 'Case breaking statements must be followed by a single blank line';
+ $fix = $phpcsFile->addFixableError($error, $nextBreak, 'SpacingAfterBreak');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($semicolon + 1); $i <= $tokens[$stackPtr]['scope_closer']; $i++) {
+ if ($tokens[$i]['line'] === $nextLine) {
+ $phpcsFile->fixer->addNewlineBefore($i);
+ break;
+ }
+
+ if ($tokens[$i]['line'] === $tokens[$semicolon]['line']) {
+ continue;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }//end if
+ } else {
+ // Ensure the BREAK statement is not followed by a blank line.
+ if ($nextLine !== ($tokens[$semicolon]['line'] + 1)) {
+ $error = 'Blank lines are not allowed after the DEFAULT case\'s breaking statement';
+ $phpcsFile->addError($error, $nextBreak, 'SpacingAfterDefaultBreak');
+ }
+ }//end if
+
+ $caseLine = $tokens[$nextCase]['line'];
+ $nextLine = $tokens[$nextBreak]['line'];
+ for ($i = ($opener + 1); $i < $nextBreak; $i++) {
+ if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
+ $nextLine = $tokens[$i]['line'];
+ break;
+ }
+ }
+
+ if ($nextLine !== ($caseLine + 1)) {
+ $error = 'Blank lines are not allowed after '.strtoupper($type).' statements';
+ $phpcsFile->addError($error, $nextCase, 'SpacingAfter'.$type);
+ }
+ }//end if
+
+ if ($tokens[$nextBreak]['code'] === T_BREAK) {
+ if ($type === 'Case') {
+ // Ensure empty CASE statements are not allowed.
+ // They must have some code content in them. A comment is not enough.
+ // But count RETURN statements as valid content if they also
+ // happen to close the CASE statement.
+ $foundContent = false;
+ for ($i = ($tokens[$nextCase]['scope_opener'] + 1); $i < $nextBreak; $i++) {
+ if ($tokens[$i]['code'] === T_CASE) {
+ $i = $tokens[$i]['scope_opener'];
+ continue;
+ }
+
+ if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === false) {
+ $foundContent = true;
+ break;
+ }
+ }
+
+ if ($foundContent === false) {
+ $error = 'Empty CASE statements are not allowed';
+ $phpcsFile->addError($error, $nextCase, 'EmptyCase');
+ }
+ } else {
+ // Ensure empty DEFAULT statements are not allowed.
+ // They must (at least) have a comment describing why
+ // the default case is being ignored.
+ $foundContent = false;
+ for ($i = ($tokens[$nextCase]['scope_opener'] + 1); $i < $nextBreak; $i++) {
+ if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
+ $foundContent = true;
+ break;
+ }
+ }
+
+ if ($foundContent === false) {
+ $error = 'Comment required for empty DEFAULT case';
+ $phpcsFile->addError($error, $nextCase, 'EmptyDefault');
+ }
+ }//end if
+ }//end if
+ } else if ($type === 'Default') {
+ $error = 'DEFAULT case must have a breaking statement';
+ $phpcsFile->addError($error, $nextCase, 'DefaultNoBreak');
+ }//end if
+ }//end while
+
+ if ($foundDefault === false) {
+ $error = 'All SWITCH statements must contain a DEFAULT case';
+ $phpcsFile->addError($error, $stackPtr, 'MissingDefault');
+ }
+
+ if ($tokens[$switch['scope_closer']]['column'] !== $switch['column']) {
+ $error = 'Closing brace of SWITCH statement must be aligned with SWITCH keyword';
+ $phpcsFile->addError($error, $switch['scope_closer'], 'CloseBraceAlign');
+ }
+
+ if ($caseCount === 0) {
+ $error = 'SWITCH statements must contain at least one CASE statement';
+ $phpcsFile->addError($error, $stackPtr, 'MissingCase');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Runs jslint.js on the file.
+ *
+ * @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\Standards\Squiz\Sniffs\Debug;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Config;
+
+class JSLintSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jslint.js could not be run
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $rhinoPath = Config::getExecutablePath('jslint');
+ $jslintPath = Config::getExecutablePath('jslint');
+ if ($rhinoPath === null || $jslintPath === null) {
+ return;
+ }
+
+ $fileName = $phpcsFile->getFilename();
+
+ $rhinoPath = escapeshellcmd($rhinoPath);
+ $jslintPath = escapeshellcmd($jslintPath);
+
+ $cmd = "$rhinoPath \"$jslintPath\" ".escapeshellarg($fileName);
+ $msg = exec($cmd, $output, $retval);
+
+ if (is_array($output) === true) {
+ foreach ($output as $finding) {
+ $matches = array();
+ $numMatches = preg_match('/Lint at line ([0-9]+).*:(.*)$/', $finding, $matches);
+ if ($numMatches === 0) {
+ continue;
+ }
+
+ $line = (int) $matches[1];
+ $message = 'jslint says: '.trim($matches[2]);
+ $phpcsFile->addWarningOnLine($message, $line, 'ExternalTool');
+ }
+ }
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Runs JavaScript Lint on the file.
+ *
+ * @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\Standards\Squiz\Sniffs\Debug;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+
+class JavaScriptLintSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $jslPath = Config::getExecutablePath('jsl');
+ if (is_null($jslPath) === true) {
+ return;
+ }
+
+ $fileName = $phpcsFile->getFilename();
+
+ $cmd = '"'.escapeshellcmd($jslPath).'" -nologo -nofilelisting -nocontext -nosummary -output-format __LINE__:__ERROR__ -process '.escapeshellarg($fileName);
+ $msg = exec($cmd, $output, $retval);
+
+ // Variable $exitCode is the last line of $output if no error occurs, on
+ // error it is numeric. Try to handle various error conditions and
+ // provide useful error reporting.
+ if ($retval === 2 || $retval === 4) {
+ if (is_array($output) === true) {
+ $msg = join('\n', $output);
+ }
+
+ throw new RuntimeException("Failed invoking JavaScript Lint, retval was [$retval], output was [$msg]");
+ }
+
+ if (is_array($output) === true) {
+ foreach ($output as $finding) {
+ $split = strpos($finding, ':');
+ $line = substr($finding, 0, $split);
+ $message = substr($finding, ($split + 1));
+ $phpcsFile->addWarningOnLine(trim($message), $line, 'ExternalTool');
+ }
+ }
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tests that classes and interfaces are not declared in .php files.
+ *
+ * @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\Standards\Squiz\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class FileExtensionSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return int
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $fileName = $phpcsFile->getFileName();
+ $extension = substr($fileName, strrpos($fileName, '.'));
+ $nextClass = $phpcsFile->findNext(array(T_CLASS, T_INTERFACE, T_TRAIT), $stackPtr);
+
+ if ($nextClass !== false) {
+ $phpcsFile->recordMetric($stackPtr, 'File extension for class files', $extension);
+ if ($extension === '.php') {
+ $error = '%s found in ".php" file; use ".inc" extension instead';
+ $data = array(ucfirst($tokens[$nextClass]['content']));
+ $phpcsFile->addError($error, $stackPtr, 'ClassFound', $data);
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'File extension for non-class files', $extension);
+ if ($extension === '.inc') {
+ $error = 'No interface or class found in ".inc" file; use ".php" extension instead';
+ $phpcsFile->addError($error, $stackPtr, 'NoClass');
+ }
+ }
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tests that all arithmetic operations are bracketed.
+ *
+ * @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\Standards\Squiz\Sniffs\Formatting;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class OperatorBracketSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return Tokens::$operators;
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 ($phpcsFile->tokenizerType === 'JS' && $tokens[$stackPtr]['code'] === T_PLUS) {
+ // JavaScript uses the plus operator for string concatenation as well
+ // so we cannot accurately determine if it is a string concat or addition.
+ // So just ignore it.
+ return;
+ }
+
+ // If the & is a reference, then we don't want to check for brackets.
+ if ($tokens[$stackPtr]['code'] === T_BITWISE_AND && $phpcsFile->isReference($stackPtr) === true) {
+ return;
+ }
+
+ // There is one instance where brackets aren't needed, which involves
+ // the minus sign being used to assign a negative number to a variable.
+ if ($tokens[$stackPtr]['code'] === T_MINUS) {
+ // Check to see if we are trying to return -n.
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] === T_RETURN) {
+ return;
+ }
+
+ $number = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$number]['code'] === T_LNUMBER || $tokens[$number]['code'] === T_DNUMBER) {
+ $previous = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($previous !== false) {
+ $isAssignment = in_array($tokens[$previous]['code'], Tokens::$assignmentTokens);
+ $isEquality = in_array($tokens[$previous]['code'], Tokens::$equalityTokens);
+ $isComparison = in_array($tokens[$previous]['code'], Tokens::$comparisonTokens);
+ if ($isAssignment === true || $isEquality === true || $isComparison === true) {
+ // This is a negative assignment or comparison.
+ // We need to check that the minus and the number are
+ // adjacent.
+ if (($number - $stackPtr) !== 1) {
+ $error = 'No space allowed between minus sign and number';
+ $phpcsFile->addError($error, $stackPtr, 'SpacingAfterMinus');
+ }
+
+ return;
+ }
+ }
+ }
+ }//end if
+
+ $previousToken = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true, null, true);
+ if ($previousToken !== false) {
+ // A list of tokens that indicate that the token is not
+ // part of an arithmetic operation.
+ $invalidTokens = array(
+ T_COMMA,
+ T_COLON,
+ T_OPEN_PARENTHESIS,
+ T_OPEN_SQUARE_BRACKET,
+ T_OPEN_SHORT_ARRAY,
+ T_CASE,
+ );
+
+ if (in_array($tokens[$previousToken]['code'], $invalidTokens) === true) {
+ return;
+ }
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_BITWISE_OR
+ && isset($tokens[$stackPtr]['nested_parenthesis']) === true
+ ) {
+ $brackets = $tokens[$stackPtr]['nested_parenthesis'];
+ $lastBracket = array_pop($brackets);
+ if (isset($tokens[$lastBracket]['parenthesis_owner']) === true
+ && $tokens[$tokens[$lastBracket]['parenthesis_owner']]['code'] === T_CATCH
+ ) {
+ // This is a pipe character inside a catch statement, so it is acting
+ // as an exception type seperator and not an arithmetic operation.
+ return;
+ }
+ }
+
+ // Tokens that are allowed inside a bracketed operation.
+ $allowed = array(
+ T_VARIABLE,
+ T_LNUMBER,
+ T_DNUMBER,
+ T_STRING,
+ T_WHITESPACE,
+ T_NS_SEPARATOR,
+ T_THIS,
+ T_SELF,
+ T_OBJECT_OPERATOR,
+ T_DOUBLE_COLON,
+ T_OPEN_SQUARE_BRACKET,
+ T_CLOSE_SQUARE_BRACKET,
+ T_MODULUS,
+ T_NONE,
+ );
+
+ $allowed += Tokens::$operators;
+
+ $lastBracket = false;
+ if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
+ $parenthesis = array_reverse($tokens[$stackPtr]['nested_parenthesis'], true);
+ foreach ($parenthesis as $bracket => $endBracket) {
+ $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, ($bracket - 1), null, true);
+ $prevCode = $tokens[$prevToken]['code'];
+
+ if ($prevCode === T_ISSET) {
+ // This operation is inside an isset() call, but has
+ // no bracket of it's own.
+ break;
+ }
+
+ if ($prevCode === T_STRING || $prevCode === T_SWITCH) {
+ // We allow simple operations to not be bracketed.
+ // For example, ceil($one / $two).
+ for ($prev = ($stackPtr - 1); $prev > $bracket; $prev--) {
+ if (in_array($tokens[$prev]['code'], $allowed) === true) {
+ continue;
+ }
+
+ if ($tokens[$prev]['code'] === T_CLOSE_PARENTHESIS) {
+ $prev = $tokens[$prev]['parenthesis_opener'];
+ } else {
+ break;
+ }
+ }
+
+ if ($prev !== $bracket) {
+ break;
+ }
+
+ for ($next = ($stackPtr + 1); $next < $endBracket; $next++) {
+ if (in_array($tokens[$next]['code'], $allowed) === true) {
+ continue;
+ }
+
+ if ($tokens[$next]['code'] === T_OPEN_PARENTHESIS) {
+ $next = $tokens[$next]['parenthesis_closer'];
+ } else {
+ break;
+ }
+ }
+
+ if ($next !== $endBracket) {
+ break;
+ }
+ }//end if
+
+ if (in_array($prevCode, Tokens::$scopeOpeners) === true) {
+ // This operation is inside a control structure like FOREACH
+ // or IF, but has no bracket of it's own.
+ // The only control structure allowed to do this is SWITCH.
+ if ($prevCode !== T_SWITCH) {
+ break;
+ }
+ }
+
+ if ($prevCode === T_OPEN_PARENTHESIS) {
+ // These are two open parenthesis in a row. If the current
+ // one doesn't enclose the operator, go to the previous one.
+ if ($endBracket < $stackPtr) {
+ continue;
+ }
+ }
+
+ $lastBracket = $bracket;
+ break;
+ }//end foreach
+ }//end if
+
+ if ($lastBracket === false) {
+ // It is not in a bracketed statement at all.
+ $this->addMissingBracketsError($phpcsFile, $stackPtr);
+ return;
+ } else if ($tokens[$lastBracket]['parenthesis_closer'] < $stackPtr) {
+ // There are a set of brackets in front of it that don't include it.
+ $this->addMissingBracketsError($phpcsFile, $stackPtr);
+ return;
+ } else {
+ // We are enclosed in a set of bracket, so the last thing to
+ // check is that we are not also enclosed in square brackets
+ // like this: ($array[$index + 1]), which is invalid.
+ $brackets = array(
+ T_OPEN_SQUARE_BRACKET,
+ T_CLOSE_SQUARE_BRACKET,
+ );
+
+ $squareBracket = $phpcsFile->findPrevious($brackets, ($stackPtr - 1), $lastBracket);
+ if ($squareBracket !== false && $tokens[$squareBracket]['code'] === T_OPEN_SQUARE_BRACKET) {
+ $closeSquareBracket = $phpcsFile->findNext($brackets, ($stackPtr + 1));
+ if ($closeSquareBracket !== false && $tokens[$closeSquareBracket]['code'] === T_CLOSE_SQUARE_BRACKET) {
+ $this->addMissingBracketsError($phpcsFile, $stackPtr);
+ }
+ }
+
+ return;
+ }//end if
+
+ $lastAssignment = $phpcsFile->findPrevious(Tokens::$assignmentTokens, $stackPtr, null, false, null, true);
+ if ($lastAssignment !== false && $lastAssignment > $lastBracket) {
+ $this->addMissingBracketsError($phpcsFile, $stackPtr);
+ }
+
+ }//end process()
+
+
+ /**
+ * Add and fix the missing brackets error.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function addMissingBracketsError($phpcsFile, $stackPtr)
+ {
+ $error = 'Arithmetic operation must be bracketed';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'MissingBrackets');
+
+ if ($fix === false) {
+ return;
+ }
+
+ $tokens = $phpcsFile->getTokens();
+
+ $allowed = array(
+ T_VARIABLE => true,
+ T_LNUMBER => true,
+ T_DNUMBER => true,
+ T_STRING => true,
+ T_WHITESPACE => true,
+ T_NS_SEPARATOR => true,
+ T_THIS => true,
+ T_SELF => true,
+ T_OBJECT_OPERATOR => true,
+ T_DOUBLE_COLON => true,
+ T_MODULUS => true,
+ T_ISSET => true,
+ T_ARRAY => true,
+ T_NONE => true,
+ );
+
+ // Find the first token in the expression.
+ for ($before = ($stackPtr - 1); $before > 0; $before--) {
+ // Special case for plus operators because we can't tell if they are used
+ // for addition or string contact. So assume string concat to be safe.
+ if ($phpcsFile->tokenizerType === 'JS' && $tokens[$before]['code'] === T_PLUS) {
+ break;
+ }
+
+ if (isset(Tokens::$emptyTokens[$tokens[$before]['code']]) === true
+ || isset(Tokens::$operators[$tokens[$before]['code']]) === true
+ || isset(Tokens::$castTokens[$tokens[$before]['code']]) === true
+ || isset($allowed[$tokens[$before]['code']]) === true
+ ) {
+ continue;
+ }
+
+ if ($tokens[$before]['code'] === T_CLOSE_PARENTHESIS) {
+ $before = $tokens[$before]['parenthesis_opener'];
+ continue;
+ }
+
+ if ($tokens[$before]['code'] === T_CLOSE_SQUARE_BRACKET) {
+ $before = $tokens[$before]['bracket_opener'];
+ continue;
+ }
+
+ if ($tokens[$before]['code'] === T_CLOSE_SHORT_ARRAY) {
+ $before = $tokens[$before]['bracket_opener'];
+ continue;
+ }
+
+ break;
+ }//end for
+
+ $before = $phpcsFile->findNext(Tokens::$emptyTokens, ($before + 1), null, true);
+
+ // Find the last token in the expression.
+ for ($after = ($stackPtr + 1); $after < $phpcsFile->numTokens; $after++) {
+ // Special case for plus operators because we can't tell if they are used
+ // for addition or string contact. So assume string concat to be safe.
+ if ($phpcsFile->tokenizerType === 'JS' && $tokens[$after]['code'] === T_PLUS) {
+ break;
+ }
+
+ if (isset(Tokens::$emptyTokens[$tokens[$after]['code']]) === true
+ || isset(Tokens::$operators[$tokens[$after]['code']]) === true
+ || isset(Tokens::$castTokens[$tokens[$after]['code']]) === true
+ || isset($allowed[$tokens[$after]['code']]) === true
+ ) {
+ continue;
+ }
+
+ if ($tokens[$after]['code'] === T_OPEN_PARENTHESIS) {
+ $after = $tokens[$after]['parenthesis_closer'];
+ continue;
+ }
+
+ if ($tokens[$after]['code'] === T_OPEN_SQUARE_BRACKET) {
+ $after = $tokens[$after]['bracket_closer'];
+ continue;
+ }
+
+ if ($tokens[$after]['code'] === T_OPEN_SHORT_ARRAY) {
+ $after = $tokens[$after]['bracket_closer'];
+ continue;
+ }
+
+ break;
+ }//end for
+
+ $after = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($after - 1), null, true);
+
+ // Can only fix this error if both tokens are available for fixing.
+ // Adding one bracket without the other will create parse errors.
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken($before, '('.$tokens[$before]['content']);
+ $phpcsFile->fixer->replaceToken($after, $tokens[$after]['content'].')');
+ $phpcsFile->fixer->endChangeset();
+
+ }//end addMissingBracketsError()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that arguments in function declarations are spaced correctly.
+ *
+ * @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\Standards\Squiz\Sniffs\Functions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class FunctionDeclarationArgumentSpacingSniff implements Sniff
+{
+
+ /**
+ * How many spaces should surround the equals signs.
+ *
+ * @var integer
+ */
+ public $equalsSpacing = 0;
+
+ /**
+ * How many spaces should follow the opening bracket.
+ *
+ * @var integer
+ */
+ public $requiredSpacesAfterOpen = 0;
+
+ /**
+ * How many spaces should precede the closing bracket.
+ *
+ * @var integer
+ */
+ public $requiredSpacesBeforeClose = 0;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_FUNCTION,
+ T_CLOSURE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[$stackPtr]['parenthesis_opener']) === false
+ || isset($tokens[$stackPtr]['parenthesis_closer']) === false
+ || $tokens[$stackPtr]['parenthesis_opener'] === null
+ || $tokens[$stackPtr]['parenthesis_closer'] === null
+ ) {
+ return;
+ }
+
+ $this->equalsSpacing = (int) $this->equalsSpacing;
+ $this->requiredSpacesAfterOpen = (int) $this->requiredSpacesAfterOpen;
+ $this->requiredSpacesBeforeClose = (int) $this->requiredSpacesBeforeClose;
+
+ $openBracket = $tokens[$stackPtr]['parenthesis_opener'];
+ $this->processBracket($phpcsFile, $openBracket);
+
+ if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
+ $use = $phpcsFile->findNext(T_USE, ($tokens[$openBracket]['parenthesis_closer'] + 1), $tokens[$stackPtr]['scope_opener']);
+ if ($use !== false) {
+ $openBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1), null);
+ $this->processBracket($phpcsFile, $openBracket);
+ }
+ }
+
+ }//end process()
+
+
+ /**
+ * Processes the contents of a single set of brackets.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $openBracket The position of the open bracket
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function processBracket($phpcsFile, $openBracket)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+ $multiLine = ($tokens[$openBracket]['line'] !== $tokens[$closeBracket]['line']);
+
+ $nextParam = $openBracket;
+ $params = array();
+ while (($nextParam = $phpcsFile->findNext(T_VARIABLE, ($nextParam + 1), $closeBracket)) !== false) {
+ $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($nextParam + 1), ($closeBracket + 1), true);
+ if ($nextToken === false) {
+ break;
+ }
+
+ $nextCode = $tokens[$nextToken]['code'];
+
+ if ($nextCode === T_EQUAL) {
+ // Check parameter default spacing.
+ $spacesBefore = 0;
+ if (($nextToken - $nextParam) > 1) {
+ $spacesBefore = strlen($tokens[($nextParam + 1)]['content']);
+ }
+
+ if ($spacesBefore !== $this->equalsSpacing) {
+ $error = 'Incorrect spacing between argument "%s" and equals sign; expected '.$this->equalsSpacing.' but found %s';
+ $data = array(
+ $tokens[$nextParam]['content'],
+ $spacesBefore,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'SpaceBeforeEquals', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $this->equalsSpacing);
+ if ($spacesBefore === 0) {
+ $phpcsFile->fixer->addContentBefore($nextToken, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($nextToken - 1), $padding);
+ }
+ }
+ }//end if
+
+ $spacesAfter = 0;
+ if ($tokens[($nextToken + 1)]['code'] === T_WHITESPACE) {
+ $spacesAfter = strlen($tokens[($nextToken + 1)]['content']);
+ }
+
+ if ($spacesAfter !== $this->equalsSpacing) {
+ $error = 'Incorrect spacing between default value and equals sign for argument "%s"; expected '.$this->equalsSpacing.' but found %s';
+ $data = array(
+ $tokens[$nextParam]['content'],
+ $spacesAfter,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'SpaceAfterDefault', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $this->equalsSpacing);
+ if ($spacesAfter === 0) {
+ $phpcsFile->fixer->addContent($nextToken, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($nextToken + 1), $padding);
+ }
+ }
+ }//end if
+ }//end if
+
+ // Find and check the comma (if there is one).
+ $nextComma = $phpcsFile->findNext(T_COMMA, ($nextParam + 1), $closeBracket);
+ if ($nextComma !== false) {
+ // Comma found.
+ if ($tokens[($nextComma - 1)]['code'] === T_WHITESPACE) {
+ $error = 'Expected 0 spaces between argument "%s" and comma; %s found';
+ $data = array(
+ $tokens[$nextParam]['content'],
+ strlen($tokens[($nextComma - 1)]['content']),
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'SpaceBeforeComma', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($nextComma - 1), '');
+ }
+ }
+ }
+
+ $checkToken = ($nextParam - 1);
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, $checkToken, null, true);
+ if ($tokens[$prev]['code'] === T_ELLIPSIS) {
+ $checkToken = ($prev - 1);
+ }
+
+ // Take references into account when expecting the
+ // location of whitespace.
+ if ($phpcsFile->isReference($checkToken) === true) {
+ $whitespace = ($checkToken - 1);
+ } else {
+ $whitespace = $checkToken;
+ }
+
+ if (empty($params) === false) {
+ // This is not the first argument in the function declaration.
+ $arg = $tokens[$nextParam]['content'];
+
+ // Before we throw an error, make sure there is no type hint.
+ $comma = $phpcsFile->findPrevious(T_COMMA, ($nextParam - 1));
+ $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($comma + 1), null, true);
+ if ($phpcsFile->isReference($nextToken) === true) {
+ $nextToken++;
+ }
+
+ $gap = 0;
+ if ($tokens[$whitespace]['code'] === T_WHITESPACE) {
+ $gap = strlen($tokens[$whitespace]['content']);
+ }
+
+ if ($nextToken !== $nextParam) {
+ // There was a type hint, so check the spacing between
+ // the hint and the variable as well.
+ $hint = $tokens[$nextToken]['content'];
+
+ if ($gap !== 1) {
+ $error = 'Expected 1 space between type hint and argument "%s"; %s found';
+ $data = array(
+ $arg,
+ $gap,
+ );
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'SpacingAfterHint', $data);
+ if ($fix === true) {
+ if ($gap === 0) {
+ $phpcsFile->fixer->addContent($whitespace, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken($whitespace, ' ');
+ }
+ }
+ }
+
+ if ($multiLine === false) {
+ if ($tokens[($comma + 1)]['code'] !== T_WHITESPACE) {
+ $error = 'Expected 1 space between comma and type hint "%s"; 0 found';
+ $data = array($hint);
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'NoSpaceBeforeHint', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($comma, ' ');
+ }
+ } else {
+ $gap = strlen($tokens[($comma + 1)]['content']);
+ if ($gap !== 1) {
+ $error = 'Expected 1 space between comma and type hint "%s"; %s found';
+ $data = array(
+ $hint,
+ $gap,
+ );
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'SpacingBeforeHint', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($comma + 1), ' ');
+ }
+ }
+ }//end if
+ }//end if
+ } else {
+ // No type hint.
+ if ($gap === 0) {
+ $error = 'Expected 1 space between comma and argument "%s"; 0 found';
+ $data = array($arg);
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'NoSpaceBeforeArg', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($whitespace, ' ');
+ }
+ } else if ($gap !== 1) {
+ // Just make sure this is not actually an indent.
+ if ($tokens[$whitespace]['line'] === $tokens[($whitespace - 1)]['line']) {
+ $error = 'Expected 1 space between comma and argument "%s"; %s found';
+ $data = array(
+ $arg,
+ $gap,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'SpacingBeforeArg', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($whitespace, ' ');
+ }
+ }
+ }//end if
+ }//end if
+ } else {
+ $gap = 0;
+ if ($tokens[$whitespace]['code'] === T_WHITESPACE) {
+ $gap = strlen($tokens[$whitespace]['content']);
+ }
+
+ $arg = $tokens[$nextParam]['content'];
+
+ // Before we throw an error, make sure there is no type hint.
+ $bracket = $phpcsFile->findPrevious(T_OPEN_PARENTHESIS, ($nextParam - 1));
+ $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($bracket + 1), null, true);
+ if ($phpcsFile->isReference($nextToken) === true) {
+ $nextToken++;
+ }
+
+ if ($tokens[$nextToken]['code'] !== T_ELLIPSIS && $nextToken !== $nextParam) {
+ // There was a type hint, so check the spacing between
+ // the hint and the variable as well.
+ $hint = $tokens[$nextToken]['content'];
+
+ if ($gap !== 1) {
+ $error = 'Expected 1 space between type hint and argument "%s"; %s found';
+ $data = array(
+ $arg,
+ $gap,
+ );
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'SpacingAfterHint', $data);
+ if ($fix === true) {
+ if ($gap === 0) {
+ $phpcsFile->fixer->addContent($nextToken, ' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($nextToken + 1), ' ');
+ }
+ }
+ }
+
+ $spaceAfterOpen = 0;
+ if ($tokens[($bracket + 1)]['code'] === T_WHITESPACE) {
+ $spaceAfterOpen = strlen($tokens[($bracket + 1)]['content']);
+ }
+
+ if ($multiLine === false && $spaceAfterOpen !== $this->requiredSpacesAfterOpen) {
+ $error = 'Expected %s spaces between opening bracket and type hint "%s"; %s found';
+ $data = array(
+ $this->requiredSpacesAfterOpen,
+ $hint,
+ $spaceAfterOpen,
+ );
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'SpacingAfterOpenHint', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $this->requiredSpacesAfterOpen);
+ if ($spaceAfterOpen === 0) {
+ $phpcsFile->fixer->addContent($openBracket, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($openBracket + 1), $padding);
+ }
+ }
+ }
+ } else if ($multiLine === false && $gap !== $this->requiredSpacesAfterOpen) {
+ $error = 'Expected %s spaces between opening bracket and argument "%s"; %s found';
+ $data = array(
+ $this->requiredSpacesAfterOpen,
+ $arg,
+ $gap,
+ );
+ $fix = $phpcsFile->addFixableError($error, $nextToken, 'SpacingAfterOpen', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $this->requiredSpacesAfterOpen);
+ if ($gap === 0) {
+ $phpcsFile->fixer->addContent($openBracket, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($openBracket + 1), $padding);
+ }
+ }
+ }//end if
+ }//end if
+
+ $params[] = $nextParam;
+ }//end while
+
+ $gap = 0;
+ if ($tokens[($closeBracket - 1)]['code'] === T_WHITESPACE) {
+ $gap = strlen($tokens[($closeBracket - 1)]['content']);
+ }
+
+ if (empty($params) === true) {
+ // There are no parameters for this function.
+ if (($closeBracket - $openBracket) !== 1) {
+ $error = 'Expected 0 spaces between brackets of function declaration; %s found';
+ $data = array($gap);
+ $fix = $phpcsFile->addFixableError($error, $openBracket, 'SpacingBetween', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($openBracket + 1), '');
+ }
+ }
+ } else if ($multiLine === false && $gap !== $this->requiredSpacesBeforeClose) {
+ $lastParam = array_pop($params);
+ $error = 'Expected %s spaces between argument "%s" and closing bracket; %s found';
+ $data = array(
+ $this->requiredSpacesBeforeClose,
+ $tokens[$lastParam]['content'],
+ $gap,
+ );
+ $fix = $phpcsFile->addFixableError($error, $closeBracket, 'SpacingBeforeClose', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $this->requiredSpacesBeforeClose);
+ if ($gap === 0) {
+ $phpcsFile->fixer->addContentBefore($closeBracket, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($closeBracket - 1), $padding);
+ }
+ }
+ }//end if
+
+ }//end processBracket()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the function declaration is correct.
+ *
+ * @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\Standards\Squiz\Sniffs\Functions;
+
+use PHP_CodeSniffer\Sniffs\AbstractPatternSniff;
+
+class FunctionDeclarationSniff extends AbstractPatternSniff
+{
+
+
+ /**
+ * Returns an array of patterns to check are correct.
+ *
+ * @return array
+ */
+ protected function getPatterns()
+ {
+ return array(
+ 'function abc(...);',
+ 'function abc(...)',
+ 'abstract function abc(...);',
+ );
+
+ }//end getPatterns()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that duplicate arguments are not used in function declarations.
+ *
+ * @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\Standards\Squiz\Sniffs\Functions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class FunctionDuplicateArgumentSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_FUNCTION);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $openBracket = $tokens[$stackPtr]['parenthesis_opener'];
+ $closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
+
+ $foundVariables = array();
+ for ($i = ($openBracket + 1); $i < $closeBracket; $i++) {
+ if ($tokens[$i]['code'] === T_VARIABLE) {
+ $variable = $tokens[$i]['content'];
+ if (in_array($variable, $foundVariables) === true) {
+ $error = 'Variable "%s" appears more than once in function declaration';
+ $data = array($variable);
+ $phpcsFile->addError($error, $i, 'Found', $data);
+ } else {
+ $foundVariables[] = $variable;
+ }
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tests for functions outside of classes.
+ *
+ * @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\Standards\Squiz\Sniffs\Functions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class GlobalFunctionSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_FUNCTION);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (empty($tokens[$stackPtr]['conditions']) === true) {
+ $functionName = $phpcsFile->getDeclarationName($stackPtr);
+ if ($functionName === null) {
+ return;
+ }
+
+ // Special exception for __autoload as it needs to be global.
+ if ($functionName !== '__autoload') {
+ $error = 'Consider putting global function "%s" in a static class';
+ $data = array($functionName);
+ $phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures all function keywords are lowercase.
+ *
+ * @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\Standards\Squiz\Sniffs\Functions;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class LowercaseFunctionKeywordsSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_FUNCTION,
+ T_PUBLIC,
+ T_PRIVATE,
+ T_PROTECTED,
+ T_STATIC,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $content = $tokens[$stackPtr]['content'];
+ if ($content !== strtolower($content)) {
+ $error = '%s keyword must be lowercase; expected "%s" but found "%s"';
+ $data = array(
+ strtoupper($content),
+ strtolower($content),
+ $content,
+ );
+ $phpcsFile->addError($error, $stackPtr, 'FoundUppercase', $data);
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure single and multi-line function declarations are defined correctly.
+ *
+ * @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\Standards\Squiz\Sniffs\Functions;
+
+use PHP_CodeSniffer\Standards\PEAR\Sniffs\Functions\FunctionDeclarationSniff as PEARFunctionDeclarationSniff;
+use PHP_CodeSniffer\Util\Tokens;
+
+class MultiLineFunctionDeclarationSniff extends PEARFunctionDeclarationSniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Determine if this is a multi-line function declaration.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $openBracket The position of the opening bracket
+ * in the stack passed in $tokens.
+ * @param array $tokens The stack of tokens that make up
+ * the file.
+ *
+ * @return void
+ */
+ public function isMultiLineDeclaration($phpcsFile, $stackPtr, $openBracket, $tokens)
+ {
+ $bracketsToCheck = array($stackPtr => $openBracket);
+
+ // Closures may use the USE keyword and so be multi-line in this way.
+ if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
+ $use = $phpcsFile->findNext(T_USE, ($tokens[$openBracket]['parenthesis_closer'] + 1), $tokens[$stackPtr]['scope_opener']);
+ if ($use !== false) {
+ $open = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1));
+ if ($open !== false) {
+ $bracketsToCheck[$use] = $open;
+ }
+ }
+ }
+
+ foreach ($bracketsToCheck as $stackPtr => $openBracket) {
+ // If the first argument is on a new line, this is a multi-line
+ // function declaration, even if there is only one argument.
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($openBracket + 1), null, true);
+ if ($tokens[$next]['line'] !== $tokens[$stackPtr]['line']) {
+ return true;
+ }
+
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+
+ $end = $phpcsFile->findEndOfStatement($openBracket + 1);
+ while ($tokens[$end]['code'] === T_COMMA) {
+ // If the next bit of code is not on the same line, this is a
+ // multi-line function declaration.
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), $closeBracket, true);
+ if ($next === false) {
+ continue(2);
+ }
+
+ if ($tokens[$next]['line'] !== $tokens[$end]['line']) {
+ return true;
+ }
+
+ $end = $phpcsFile->findEndOfStatement($next);
+ }
+
+ // We've reached the last argument, so see if the next content
+ // (should be the close bracket) is also on the same line.
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), $closeBracket, true);
+ if ($next !== false && $tokens[$next]['line'] !== $tokens[$end]['line']) {
+ return true;
+ }
+ }//end foreach
+
+ return false;
+
+ }//end isMultiLineDeclaration()
+
+
+ /**
+ * Processes multi-line declarations.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param array $tokens The stack of tokens that make up
+ * the file.
+ *
+ * @return void
+ */
+ public function processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens)
+ {
+ // We do everything the parent sniff does, and a bit more.
+ parent::processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens);
+
+ $openBracket = $tokens[$stackPtr]['parenthesis_opener'];
+ $this->processBracket($phpcsFile, $openBracket, $tokens, 'function');
+
+ if ($tokens[$stackPtr]['code'] !== T_CLOSURE) {
+ return;
+ }
+
+ $use = $phpcsFile->findNext(T_USE, ($tokens[$stackPtr]['parenthesis_closer'] + 1), $tokens[$stackPtr]['scope_opener']);
+ if ($use === false) {
+ return;
+ }
+
+ $openBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1), null);
+ $this->processBracket($phpcsFile, $openBracket, $tokens, 'use');
+
+ // Also check spacing.
+ if ($tokens[($use - 1)]['code'] === T_WHITESPACE) {
+ $gap = strlen($tokens[($use - 1)]['content']);
+ } else {
+ $gap = 0;
+ }
+
+ }//end processMultiLineDeclaration()
+
+
+ /**
+ * Processes the contents of a single set of brackets.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $openBracket The position of the open bracket
+ * in the stack passed in $tokens.
+ * @param array $tokens The stack of tokens that make up
+ * the file.
+ * @param string $type The type of the token the brackets
+ * belong to (function or use).
+ *
+ * @return void
+ */
+ public function processBracket($phpcsFile, $openBracket, $tokens, $type='function')
+ {
+ $errorPrefix = '';
+ if ($type === 'use') {
+ $errorPrefix = 'Use';
+ }
+
+ $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+
+ // The open bracket should be the last thing on the line.
+ if ($tokens[$openBracket]['line'] !== $tokens[$closeBracket]['line']) {
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($openBracket + 1), null, true);
+ if ($tokens[$next]['line'] !== ($tokens[$openBracket]['line'] + 1)) {
+ $error = 'The first parameter of a multi-line '.$type.' declaration must be on the line after the opening bracket';
+ $fix = $phpcsFile->addFixableError($error, $next, $errorPrefix.'FirstParamSpacing');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewline($openBracket);
+ }
+ }
+ }
+
+ // Each line between the brackets should contain a single parameter.
+ $lastComma = null;
+ for ($i = ($openBracket + 1); $i < $closeBracket; $i++) {
+ // Skip brackets, like arrays, as they can contain commas.
+ if (isset($tokens[$i]['bracket_opener']) === true) {
+ $i = $tokens[$i]['bracket_closer'];
+ continue;
+ }
+
+ if (isset($tokens[$i]['parenthesis_opener']) === true) {
+ $i = $tokens[$i]['parenthesis_closer'];
+ continue;
+ }
+
+ if ($tokens[$i]['code'] !== T_COMMA) {
+ continue;
+ }
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), null, true);
+ if ($tokens[$next]['line'] === $tokens[$i]['line']) {
+ $error = 'Multi-line '.$type.' declarations must define one parameter per line';
+ $fix = $phpcsFile->addFixableError($error, $next, $errorPrefix.'OneParamPerLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewline($i);
+ }
+ }
+ }//end for
+
+ }//end processBracket()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures method names are correct.
+ *
+ * @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\Standards\Squiz\Sniffs\NamingConventions;
+
+use PHP_CodeSniffer\Standards\PEAR\Sniffs\NamingConventions\ValidFunctionNameSniff as PEARValidFunctionNameSniff;
+use PHP_CodeSniffer\Util\Common;
+use PHP_CodeSniffer\Files\File;
+
+class ValidFunctionNameSniff extends PEARValidFunctionNameSniff
+{
+
+
+ /**
+ * Processes the tokens outside the scope.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed.
+ * @param int $stackPtr The position where this token was
+ * found.
+ *
+ * @return void
+ */
+ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
+ {
+ $functionName = $phpcsFile->getDeclarationName($stackPtr);
+ if ($functionName === null) {
+ return;
+ }
+
+ $errorData = array($functionName);
+
+ // Does this function claim to be magical?
+ if (preg_match('|^__[^_]|', $functionName) !== 0) {
+ $error = 'Function name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore';
+ $phpcsFile->addError($error, $stackPtr, 'DoubleUnderscore', $errorData);
+ return;
+ }
+
+ if (Common::isCamelCaps($functionName, false, true, false) === false) {
+ $error = 'Function name "%s" is not in camel caps format';
+ $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData);
+ }
+
+ }//end processTokenOutsideScope()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the naming of variables and member variables.
+ *
+ * @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\Standards\Squiz\Sniffs\NamingConventions;
+
+use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
+use PHP_CodeSniffer\Util\Common;
+use PHP_CodeSniffer\Files\File;
+
+class ValidVariableNameSniff extends AbstractVariableSniff
+{
+
+ /**
+ * Tokens to ignore so that we can find a DOUBLE_COLON.
+ *
+ * @var array
+ */
+ private $ignore = array(
+ T_WHITESPACE,
+ T_COMMENT,
+ );
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ protected function processVariable(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $varName = ltrim($tokens[$stackPtr]['content'], '$');
+
+ $phpReservedVars = array(
+ '_SERVER',
+ '_GET',
+ '_POST',
+ '_REQUEST',
+ '_SESSION',
+ '_ENV',
+ '_COOKIE',
+ '_FILES',
+ 'GLOBALS',
+ 'http_response_header',
+ 'HTTP_RAW_POST_DATA',
+ 'php_errormsg',
+ );
+
+ // If it's a php reserved var, then its ok.
+ if (in_array($varName, $phpReservedVars) === true) {
+ return;
+ }
+
+ $objOperator = $phpcsFile->findNext(array(T_WHITESPACE), ($stackPtr + 1), null, true);
+ if ($tokens[$objOperator]['code'] === T_OBJECT_OPERATOR) {
+ // Check to see if we are using a variable from an object.
+ $var = $phpcsFile->findNext(array(T_WHITESPACE), ($objOperator + 1), null, true);
+ if ($tokens[$var]['code'] === T_STRING) {
+ $bracket = $phpcsFile->findNext(array(T_WHITESPACE), ($var + 1), null, true);
+ if ($tokens[$bracket]['code'] !== T_OPEN_PARENTHESIS) {
+ $objVarName = $tokens[$var]['content'];
+
+ // There is no way for us to know if the var is public or
+ // private, so we have to ignore a leading underscore if there is
+ // one and just check the main part of the variable name.
+ $originalVarName = $objVarName;
+ if (substr($objVarName, 0, 1) === '_') {
+ $objVarName = substr($objVarName, 1);
+ }
+
+ if (Common::isCamelCaps($objVarName, false, true, false) === false) {
+ $error = 'Variable "%s" is not in valid camel caps format';
+ $data = array($originalVarName);
+ $phpcsFile->addError($error, $var, 'NotCamelCaps', $data);
+ }
+ }//end if
+ }//end if
+ }//end if
+
+ // There is no way for us to know if the var is public or private,
+ // so we have to ignore a leading underscore if there is one and just
+ // check the main part of the variable name.
+ $originalVarName = $varName;
+ if (substr($varName, 0, 1) === '_') {
+ $objOperator = $phpcsFile->findPrevious(array(T_WHITESPACE), ($stackPtr - 1), null, true);
+ if ($tokens[$objOperator]['code'] === T_DOUBLE_COLON) {
+ // The variable lives within a class, and is referenced like
+ // this: MyClass::$_variable, so we don't know its scope.
+ $inClass = true;
+ } else {
+ $inClass = $phpcsFile->hasCondition($stackPtr, array(T_CLASS, T_INTERFACE, T_TRAIT));
+ }
+
+ if ($inClass === true) {
+ $varName = substr($varName, 1);
+ }
+ }
+
+ if (Common::isCamelCaps($varName, false, true, false) === false) {
+ $error = 'Variable "%s" is not in valid camel caps format';
+ $data = array($originalVarName);
+ $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $data);
+ }
+
+ }//end processVariable()
+
+
+ /**
+ * Processes class member variables.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ protected function processMemberVar(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $varName = ltrim($tokens[$stackPtr]['content'], '$');
+ $memberProps = $phpcsFile->getMemberProperties($stackPtr);
+ if (empty($memberProps) === true) {
+ // Couldn't get any info about this variable, which
+ // generally means it is invalid or possibly has a parse
+ // error. Any errors will be reported by the core, so
+ // we can ignore it.
+ return;
+ }
+
+ $public = ($memberProps['scope'] !== 'private');
+ $errorData = array($varName);
+
+ if ($public === true) {
+ if (substr($varName, 0, 1) === '_') {
+ $error = '%s member variable "%s" must not contain a leading underscore';
+ $data = array(
+ ucfirst($memberProps['scope']),
+ $errorData[0],
+ );
+ $phpcsFile->addError($error, $stackPtr, 'PublicHasUnderscore', $data);
+ return;
+ }
+ } else {
+ if (substr($varName, 0, 1) !== '_') {
+ $error = 'Private member variable "%s" must contain a leading underscore';
+ $phpcsFile->addError($error, $stackPtr, 'PrivateNoUnderscore', $errorData);
+ return;
+ }
+ }
+
+ if (Common::isCamelCaps($varName, false, $public, false) === false) {
+ $error = 'Member variable "%s" is not in valid camel caps format';
+ $phpcsFile->addError($error, $stackPtr, 'MemberNotCamelCaps', $errorData);
+ }
+
+ }//end processMemberVar()
+
+
+ /**
+ * Processes the variable found within a double quoted string.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the double quoted
+ * string.
+ *
+ * @return void
+ */
+ protected function processVariableInString(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $phpReservedVars = array(
+ '_SERVER',
+ '_GET',
+ '_POST',
+ '_REQUEST',
+ '_SESSION',
+ '_ENV',
+ '_COOKIE',
+ '_FILES',
+ 'GLOBALS',
+ 'http_response_header',
+ 'HTTP_RAW_POST_DATA',
+ 'php_errormsg',
+ );
+
+ if (preg_match_all('|[^\\\]\${?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)|', $tokens[$stackPtr]['content'], $matches) !== 0) {
+ foreach ($matches[1] as $varName) {
+ // If it's a php reserved var, then its ok.
+ if (in_array($varName, $phpReservedVars) === true) {
+ continue;
+ }
+
+ if (Common::isCamelCaps($varName, false, true, false) === false) {
+ $error = 'Variable "%s" is not in valid camel caps format';
+ $data = array($varName);
+ $phpcsFile->addError($error, $stackPtr, 'StringNotCamelCaps', $data);
+ }
+ }
+ }
+
+ }//end processVariableInString()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that object indexes are written in dot notation.
+ *
+ * @author Sertan Danis <sdanis@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\Standards\Squiz\Sniffs\Objects;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DisallowObjectStringIndexSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_SQUARE_BRACKET);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // Check if the next non whitespace token is a string.
+ $index = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$index]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
+ return;
+ }
+
+ // Make sure it is the only thing in the square brackets.
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($index + 1), null, true);
+ if ($tokens[$next]['code'] !== T_CLOSE_SQUARE_BRACKET) {
+ return;
+ }
+
+ // Allow indexes that have dots in them because we can't write
+ // them in dot notation.
+ $content = trim($tokens[$index]['content'], '"\' ');
+ if (strpos($content, '.') !== false) {
+ return;
+ }
+
+ // Also ignore reserved words.
+ if ($content === 'super') {
+ return;
+ }
+
+ // Token before the opening square bracket cannot be a var name.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] === T_STRING) {
+ $error = 'Object indexes must be written in dot notation';
+ $phpcsFile->addError($error, $prev, 'Found');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures objects are assigned to a variable when instantiated.
+ *
+ * @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\Standards\Squiz\Sniffs\Objects;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ObjectInstantiationSniff implements Sniff
+{
+
+
+ /**
+ * Registers the token types that this sniff wishes to listen to.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_NEW);
+
+ }//end register()
+
+
+ /**
+ * Process the tokens that this sniff is listening for.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $allowedTokens = Tokens::$emptyTokens;
+ $allowedTokens[] = T_BITWISE_AND;
+
+ $prev = $phpcsFile->findPrevious($allowedTokens, ($stackPtr - 1), null, true);
+
+ $allowedTokens = array(
+ T_EQUAL => true,
+ T_DOUBLE_ARROW => true,
+ T_THROW => true,
+ T_RETURN => true,
+ T_INLINE_THEN => true,
+ T_INLINE_ELSE => true,
+ );
+
+ if (isset($allowedTokens[$tokens[$prev]['code']]) === false) {
+ $error = 'New objects must be assigned to a variable';
+ $phpcsFile->addError($error, $stackPtr, 'NotAssigned');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures the last member of an object is not followed by a comma.
+ *
+ * @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\Standards\Squiz\Sniffs\Objects;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ObjectMemberCommaSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * Registers the token types that this sniff wishes to listen to.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_CLOSE_OBJECT);
+
+ }//end register()
+
+
+ /**
+ * Process the tokens that this sniff is listening for.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] === T_COMMA) {
+ $error = 'Last member of object must not be followed by a comma';
+ $fix = $phpcsFile->addFixableError($error, $prev, 'Missing');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($prev, '');
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * A Sniff to enforce the use of IDENTICAL type operators rather than EQUAL operators.
+ *
+ * @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\Standards\Squiz\Sniffs\Operators;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ComparisonOperatorUsageSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+ /**
+ * A list of valid comparison operators.
+ *
+ * @var array
+ */
+ private static $validOps = array(
+ T_IS_IDENTICAL,
+ T_IS_NOT_IDENTICAL,
+ T_LESS_THAN,
+ T_GREATER_THAN,
+ T_IS_GREATER_OR_EQUAL,
+ T_IS_SMALLER_OR_EQUAL,
+ T_INSTANCEOF,
+ );
+
+ /**
+ * A list of invalid operators with their alternatives.
+ *
+ * @var array<int, string>
+ */
+ private static $invalidOps = array(
+ 'PHP' => array(
+ T_IS_EQUAL => '===',
+ T_IS_NOT_EQUAL => '!==',
+ T_BOOLEAN_NOT => '=== FALSE',
+ ),
+ 'JS' => array(
+ T_IS_EQUAL => '===',
+ T_IS_NOT_EQUAL => '!==',
+ ),
+ );
+
+
+ /**
+ * Registers the token types that this sniff wishes to listen to.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_IF,
+ T_ELSEIF,
+ T_INLINE_THEN,
+ T_WHILE,
+ T_FOR,
+ );
+
+ }//end register()
+
+
+ /**
+ * Process the tokens that this sniff is listening for.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where the token
+ * was found.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $tokenizer = $phpcsFile->tokenizerType;
+
+ if ($tokens[$stackPtr]['code'] === T_INLINE_THEN) {
+ $end = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$end]['code'] !== T_CLOSE_PARENTHESIS) {
+ // This inline IF statement does not have its condition
+ // bracketed, so we need to guess where it starts.
+ for ($i = ($end - 1); $i >= 0; $i--) {
+ if ($tokens[$i]['code'] === T_SEMICOLON) {
+ // Stop here as we assume it is the end
+ // of the previous statement.
+ break;
+ } else if ($tokens[$i]['code'] === T_OPEN_TAG) {
+ // Stop here as this is the start of the file.
+ break;
+ } else if ($tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET) {
+ // Stop if this is the closing brace of
+ // a code block.
+ if (isset($tokens[$i]['scope_opener']) === true) {
+ break;
+ }
+ } else if ($tokens[$i]['code'] === T_OPEN_CURLY_BRACKET) {
+ // Stop if this is the opening brace of
+ // a code block.
+ if (isset($tokens[$i]['scope_closer']) === true) {
+ break;
+ }
+ }
+ }//end for
+
+ $start = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
+ } else {
+ if (isset($tokens[$end]['parenthesis_opener']) === false) {
+ return;
+ }
+
+ $start = $tokens[$end]['parenthesis_opener'];
+ }//end if
+ } else if ($tokens[$stackPtr]['code'] === T_FOR) {
+ if (isset($tokens[$stackPtr]['parenthesis_opener']) === false) {
+ return;
+ }
+
+ $openingBracket = $tokens[$stackPtr]['parenthesis_opener'];
+ $closingBracket = $tokens[$stackPtr]['parenthesis_closer'];
+
+ $start = $phpcsFile->findNext(T_SEMICOLON, $openingBracket, $closingBracket);
+ $end = $phpcsFile->findNext(T_SEMICOLON, ($start + 1), $closingBracket);
+ if ($start === false || $end === false) {
+ return;
+ }
+ } else {
+ if (isset($tokens[$stackPtr]['parenthesis_opener']) === false) {
+ return;
+ }
+
+ $start = $tokens[$stackPtr]['parenthesis_opener'];
+ $end = $tokens[$stackPtr]['parenthesis_closer'];
+ }//end if
+
+ $requiredOps = 0;
+ $foundOps = 0;
+ $foundBools = 0;
+
+ for ($i = $start; $i <= $end; $i++) {
+ $type = $tokens[$i]['code'];
+ if (in_array($type, array_keys(self::$invalidOps[$tokenizer])) === true) {
+ $error = 'Operator %s prohibited; use %s instead';
+ $data = array(
+ $tokens[$i]['content'],
+ self::$invalidOps[$tokenizer][$type],
+ );
+ $phpcsFile->addError($error, $i, 'NotAllowed', $data);
+ $foundOps++;
+ } else if (in_array($type, self::$validOps) === true) {
+ $foundOps++;
+ }
+
+ if ($tokens[$i]['code'] === T_TRUE || $tokens[$i]['code'] === T_FALSE) {
+ $foundBools++;
+ }
+
+ if ($phpcsFile->tokenizerType !== 'JS'
+ && ($tokens[$i]['code'] === T_BOOLEAN_AND
+ || $tokens[$i]['code'] === T_BOOLEAN_OR)
+ ) {
+ $requiredOps++;
+
+ // When the instanceof operator is used with another operator
+ // like ===, you can get more ops than are required.
+ if ($foundOps > $requiredOps) {
+ $foundOps = $requiredOps;
+ }
+
+ // If we get to here and we have not found the right number of
+ // comparison operators, then we must have had an implicit
+ // true operation i.e., if ($a) instead of the required
+ // if ($a === true), so let's add an error.
+ if ($requiredOps !== $foundOps) {
+ $error = 'Implicit true comparisons prohibited; use === TRUE instead';
+ $phpcsFile->addError($error, $stackPtr, 'ImplicitTrue');
+ $foundOps++;
+ }
+ }
+ }//end for
+
+ $requiredOps++;
+
+ if ($phpcsFile->tokenizerType !== 'JS'
+ && $foundOps < $requiredOps
+ && ($requiredOps !== $foundBools)
+ ) {
+ $error = 'Implicit true comparisons prohibited; use === TRUE instead';
+ $phpcsFile->addError($error, $stackPtr, 'ImplicitTrue');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that the ++ operators are used when possible.
+ *
+ * @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\Standards\Squiz\Sniffs\Operators;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class IncrementDecrementUsageSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_EQUAL,
+ T_PLUS_EQUAL,
+ T_MINUS_EQUAL,
+ T_INC,
+ T_DEC,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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_INC || $tokens[$stackPtr]['code'] === T_DEC) {
+ $this->processIncDec($phpcsFile, $stackPtr);
+ } else {
+ $this->processAssignment($phpcsFile, $stackPtr);
+ }
+
+ }//end process()
+
+
+ /**
+ * Checks to ensure increment and decrement operators are not confusing.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ protected function processIncDec($phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Work out where the variable is so we know where to
+ // start looking for other operators.
+ if ($tokens[($stackPtr - 1)]['code'] === T_VARIABLE
+ || ($tokens[($stackPtr - 1)]['code'] === T_STRING
+ && $tokens[($stackPtr - 2)]['code'] === T_OBJECT_OPERATOR)
+ ) {
+ $start = ($stackPtr + 1);
+ } else {
+ $start = ($stackPtr + 2);
+ }
+
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, $start, null, true);
+ if ($next === false) {
+ return;
+ }
+
+ if (isset(Tokens::$arithmeticTokens[$tokens[$next]['code']]) === true) {
+ $error = 'Increment and decrement operators cannot be used in an arithmetic operation';
+ $phpcsFile->addError($error, $stackPtr, 'NotAllowed');
+ return;
+ }
+
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($start - 3), null, true);
+ if ($prev === false) {
+ return;
+ }
+
+ // Check if this is in a string concat.
+ if ($tokens[$next]['code'] === T_STRING_CONCAT || $tokens[$prev]['code'] === T_STRING_CONCAT) {
+ $error = 'Increment and decrement operators must be bracketed when used in string concatenation';
+ $phpcsFile->addError($error, $stackPtr, 'NoBrackets');
+ }
+
+ }//end processIncDec()
+
+
+ /**
+ * Checks to ensure increment and decrement operators are used.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ protected function processAssignment($phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $assignedVar = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ // Not an assignment, return.
+ if ($tokens[$assignedVar]['code'] !== T_VARIABLE) {
+ return;
+ }
+
+ $statementEnd = $phpcsFile->findNext(array(T_SEMICOLON, T_CLOSE_PARENTHESIS, T_CLOSE_SQUARE_BRACKET, T_CLOSE_CURLY_BRACKET), $stackPtr);
+
+ // If there is anything other than variables, numbers, spaces or operators we need to return.
+ $noiseTokens = $phpcsFile->findNext(array(T_LNUMBER, T_VARIABLE, T_WHITESPACE, T_PLUS, T_MINUS, T_OPEN_PARENTHESIS), ($stackPtr + 1), $statementEnd, true);
+
+ if ($noiseTokens !== false) {
+ return;
+ }
+
+ // If we are already using += or -=, we need to ignore
+ // the statement if a variable is being used.
+ if ($tokens[$stackPtr]['code'] !== T_EQUAL) {
+ $nextVar = $phpcsFile->findNext(T_VARIABLE, ($stackPtr + 1), $statementEnd);
+ if ($nextVar !== false) {
+ return;
+ }
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_EQUAL) {
+ $nextVar = ($stackPtr + 1);
+ $previousVariable = ($stackPtr + 1);
+ $variableCount = 0;
+ while (($nextVar = $phpcsFile->findNext(T_VARIABLE, ($nextVar + 1), $statementEnd)) !== false) {
+ $previousVariable = $nextVar;
+ $variableCount++;
+ }
+
+ if ($variableCount !== 1) {
+ return;
+ }
+
+ $nextVar = $previousVariable;
+ if ($tokens[$nextVar]['content'] !== $tokens[$assignedVar]['content']) {
+ return;
+ }
+ }
+
+ // We have only one variable, and it's the same as what is being assigned,
+ // so we need to check what is being added or subtracted.
+ $nextNumber = ($stackPtr + 1);
+ $previousNumber = ($stackPtr + 1);
+ $numberCount = 0;
+ while (($nextNumber = $phpcsFile->findNext(array(T_LNUMBER), ($nextNumber + 1), $statementEnd, false)) !== false) {
+ $previousNumber = $nextNumber;
+ $numberCount++;
+ }
+
+ if ($numberCount !== 1) {
+ return;
+ }
+
+ $nextNumber = $previousNumber;
+ if ($tokens[$nextNumber]['content'] === '1') {
+ if ($tokens[$stackPtr]['code'] === T_EQUAL) {
+ $opToken = $phpcsFile->findNext(array(T_PLUS, T_MINUS), ($nextVar + 1), $statementEnd);
+ if ($opToken === false) {
+ // Operator was before the variable, like:
+ // $var = 1 + $var;
+ // So we ignore it.
+ return;
+ }
+
+ $operator = $tokens[$opToken]['content'];
+ } else {
+ $operator = substr($tokens[$stackPtr]['content'], 0, 1);
+ }
+
+ // If we are adding or subtracting negative value, the operator
+ // needs to be reversed.
+ if ($tokens[$stackPtr]['code'] !== T_EQUAL) {
+ $negative = $phpcsFile->findPrevious(T_MINUS, ($nextNumber - 1), $stackPtr);
+ if ($negative !== false) {
+ if ($operator === '+') {
+ $operator = '-';
+ } else {
+ $operator = '+';
+ }
+ }
+ }
+
+ $expected = $tokens[$assignedVar]['content'].$operator.$operator;
+ $found = $phpcsFile->getTokensAsString($assignedVar, ($statementEnd - $assignedVar + 1));
+
+ if ($operator === '+') {
+ $error = 'Increment';
+ } else {
+ $error = 'Decrement';
+ }
+
+ $error .= " operators should be used where possible; found \"$found\" but expected \"$expected\"";
+ $phpcsFile->addError($error, $stackPtr, 'Found');
+ }//end if
+
+ }//end processAssignment()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures logical operators 'and' and 'or' are not used.
+ *
+ * @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\Standards\Squiz\Sniffs\Operators;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ValidLogicalOperatorsSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_LOGICAL_AND,
+ T_LOGICAL_OR,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being scanned.
+ * @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();
+
+ $replacements = array(
+ 'and' => '&&',
+ 'or' => '||',
+ );
+
+ $operator = strtolower($tokens[$stackPtr]['content']);
+ if (isset($replacements[$operator]) === false) {
+ return;
+ }
+
+ $error = 'Logical operator "%s" is prohibited; use "%s" instead';
+ $data = array(
+ $operator,
+ $replacements[$operator],
+ );
+ $phpcsFile->addError($error, $stackPtr, 'NotAllowed', $data);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Warn about commented out 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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Exceptions\TokenizerException;
+
+class CommentedOutCodeSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'CSS',
+ );
+
+ /**
+ * If a comment is more than $maxPercentage% code, a warning will be shown.
+ *
+ * @var integer
+ */
+ public $maxPercentage = 35;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_COMMENT);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // Process whole comment blocks at once, so skip all but the first token.
+ if ($stackPtr > 0 && $tokens[$stackPtr]['code'] === $tokens[($stackPtr - 1)]['code']) {
+ return;
+ }
+
+ // Ignore comments at the end of code blocks.
+ if (substr($tokens[$stackPtr]['content'], 0, 6) === '//end ') {
+ return;
+ }
+
+ $content = '';
+ if ($phpcsFile->tokenizerType === 'PHP') {
+ $content = '<?php ';
+ }
+
+ for ($i = $stackPtr; $i < $phpcsFile->numTokens; $i++) {
+ if ($tokens[$stackPtr]['code'] !== $tokens[$i]['code']) {
+ break;
+ }
+
+ /*
+ Trim as much off the comment as possible so we don't
+ have additional whitespace tokens or comment tokens
+ */
+
+ $tokenContent = trim($tokens[$i]['content']);
+
+ if (substr($tokenContent, 0, 2) === '//') {
+ $tokenContent = substr($tokenContent, 2);
+ }
+
+ if (substr($tokenContent, 0, 1) === '#') {
+ $tokenContent = substr($tokenContent, 1);
+ }
+
+ if (substr($tokenContent, 0, 3) === '/**') {
+ $tokenContent = substr($tokenContent, 3);
+ }
+
+ if (substr($tokenContent, 0, 2) === '/*') {
+ $tokenContent = substr($tokenContent, 2);
+ }
+
+ if (substr($tokenContent, -2) === '*/') {
+ $tokenContent = substr($tokenContent, 0, -2);
+ }
+
+ if (substr($tokenContent, 0, 1) === '*') {
+ $tokenContent = substr($tokenContent, 1);
+ }
+
+ $content .= $tokenContent.$phpcsFile->eolChar;
+ }//end for
+
+ $content = trim($content);
+
+ if ($phpcsFile->tokenizerType === 'PHP') {
+ $content .= ' ?>';
+ }
+
+ // Quite a few comments use multiple dashes, equals signs etc
+ // to frame comments and licence headers.
+ $content = preg_replace('/[-=*]+/', '-', $content);
+
+ // Random numbers sitting inside the content can throw parse errors
+ // for invalid literals in PHP7+, so strip those.
+ $content = preg_replace('/\d+/', '', $content);
+
+ // Because we are not really parsing code, the tokenizer can throw all sorts
+ // of errors that don't mean anything, so ignore them.
+ $oldErrors = ini_get('error_reporting');
+ ini_set('error_reporting', 0);
+ try {
+ $tokenizerClass = get_class($phpcsFile->tokenizer);
+ $tokenizer = new $tokenizerClass($content, $phpcsFile->config, $phpcsFile->eolChar);
+ $stringTokens = $tokenizer->getTokens();
+ } catch (TokenizerException $e) {
+ // We couldn't check the comment, so ignore it.
+ ini_set('error_reporting', $oldErrors);
+ return;
+ }
+
+ ini_set('error_reporting', $oldErrors);
+
+ $emptyTokens = array(
+ T_WHITESPACE => true,
+ T_STRING => true,
+ T_STRING_CONCAT => true,
+ T_ENCAPSED_AND_WHITESPACE => true,
+ T_NONE => true,
+ T_COMMENT => true,
+ );
+
+ $numTokens = count($stringTokens);
+
+ /*
+ We know what the first two and last two tokens should be
+ (because we put them there) so ignore this comment if those
+ tokens were not parsed correctly. It obviously means this is not
+ valid code.
+ */
+
+ // First token is always the opening PHP tag.
+ if ($stringTokens[0]['code'] !== T_OPEN_TAG) {
+ return;
+ }
+
+ // Last token is always the closing PHP tag, unless something went wrong.
+ if (isset($stringTokens[($numTokens - 1)]) === false
+ || $stringTokens[($numTokens - 1)]['code'] !== T_CLOSE_TAG
+ ) {
+ return;
+ }
+
+ // Second last token is always whitespace or a comment, depending
+ // on the code inside the comment.
+ if ($phpcsFile->tokenizerType === 'PHP'
+ && isset(Tokens::$emptyTokens[$stringTokens[($numTokens - 2)]['code']]) === false
+ ) {
+ return;
+ }
+
+ $numComment = 0;
+ $numPossible = 0;
+ $numCode = 0;
+
+ for ($i = 0; $i < $numTokens; $i++) {
+ if (isset($emptyTokens[$stringTokens[$i]['code']]) === true) {
+ // Looks like comment.
+ $numComment++;
+ } else if (in_array($stringTokens[$i]['code'], Tokens::$comparisonTokens) === true
+ || in_array($stringTokens[$i]['code'], Tokens::$arithmeticTokens) === true
+ || $stringTokens[$i]['code'] === T_GOTO_LABEL
+ ) {
+ // Commented out HTML/XML and other docs contain a lot of these
+ // characters, so it is best to not use them directly.
+ $numPossible++;
+ } else {
+ // Looks like code.
+ $numCode++;
+ }
+ }
+
+ // We subtract 3 from the token number so we ignore the start/end tokens
+ // and their surrounding whitespace. We take 2 off the number of code
+ // tokens so we ignore the start/end tokens.
+ if ($numTokens > 3) {
+ $numTokens -= 3;
+ }
+
+ if ($numCode >= 2) {
+ $numCode -= 2;
+ }
+
+ $percentCode = ceil((($numCode / $numTokens) * 100));
+ if ($percentCode > $this->maxPercentage) {
+ // Just in case.
+ $percentCode = min(100, $percentCode);
+
+ $error = 'This comment is %s%% valid code; is this commented out code?';
+ $data = array($percentCode);
+ $phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that boolean operators are only used inside control structure conditions.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class DisallowBooleanStatementSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return Tokens::$booleanOperators;
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
+ $foundOwner = false;
+ foreach ($tokens[$stackPtr]['nested_parenthesis'] as $open => $close) {
+ if (isset($tokens[$open]['parenthesis_owner']) === true) {
+ // Any owner means we are not just a simple statement.
+ return;
+ }
+ }
+ }
+
+ $error = 'Boolean operators are not allowed outside of control structure conditions';
+ $phpcsFile->addError($error, $stackPtr, 'Found');
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that the value of a comparison is not assigned to a variable.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class DisallowComparisonAssignmentSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_EQUAL);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // Ignore default value assignments in function definitions.
+ $function = $phpcsFile->findPrevious(T_FUNCTION, ($stackPtr - 1), null, false, null, true);
+ if ($function !== false) {
+ $opener = $tokens[$function]['parenthesis_opener'];
+ $closer = $tokens[$function]['parenthesis_closer'];
+ if ($opener < $stackPtr && $closer > $stackPtr) {
+ return;
+ }
+ }
+
+ // Ignore values in array definitions.
+ $array = $phpcsFile->findNext(
+ T_ARRAY,
+ ($stackPtr + 1),
+ null,
+ false,
+ null,
+ true
+ );
+
+ if ($array !== false) {
+ return;
+ }
+
+ // Ignore function calls.
+ $ignore = array(
+ T_STRING,
+ T_WHITESPACE,
+ T_OBJECT_OPERATOR,
+ );
+
+ $next = $phpcsFile->findNext($ignore, ($stackPtr + 1), null, true);
+ if ($tokens[$next]['code'] === T_OPEN_PARENTHESIS
+ && $tokens[($next - 1)]['code'] === T_STRING
+ ) {
+ // Code will look like: $var = myFunction(
+ // and will be ignored.
+ return;
+ }
+
+ $endStatement = $phpcsFile->findNext(T_SEMICOLON, ($stackPtr + 1));
+ if ($tokens[$stackPtr]['conditions'] !== $tokens[$endStatement]['conditions']) {
+ // This statement doesn't end with a semicolon, which is the case for
+ // the last expression in a for loop.
+ return;
+ }
+
+ for ($i = ($stackPtr + 1); $i < $endStatement; $i++) {
+ if (isset(Tokens::$comparisonTokens[$tokens[$i]['code']]) === true
+ || $tokens[$i]['code'] === T_INLINE_THEN
+ ) {
+ $error = 'The value of a comparison must not be assigned to a variable';
+ $phpcsFile->addError($error, $stackPtr, 'AssignedComparison');
+ break;
+ }
+
+ if (isset(Tokens::$booleanOperators[$tokens[$i]['code']]) === true
+ || $tokens[$i]['code'] === T_BOOLEAN_NOT
+ ) {
+ $error = 'The value of a boolean operation must not be assigned to a variable';
+ $phpcsFile->addError($error, $stackPtr, 'AssignedBool');
+ break;
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Stops inline IF statements from being used.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DisallowInlineIfSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_INLINE_THEN);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $phpcsFile->addError('Inline IF statements are not allowed', $stackPtr, 'Found');
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures there is only one assignment on a line, and that it is the first thing on the 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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class DisallowMultipleAssignmentsSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_EQUAL);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // Ignore default value assignments in function definitions.
+ $function = $phpcsFile->findPrevious(array(T_FUNCTION, T_CLOSURE), ($stackPtr - 1), null, false, null, true);
+ if ($function !== false) {
+ $opener = $tokens[$function]['parenthesis_opener'];
+ $closer = $tokens[$function]['parenthesis_closer'];
+ if ($opener < $stackPtr && $closer > $stackPtr) {
+ return;
+ }
+ }
+
+ /*
+ The general rule is:
+ Find an equal sign and go backwards along the line. If you hit an
+ end bracket, skip to the opening bracket. When you find a variable,
+ stop. That variable must be the first non-empty token on the line
+ or in the statement. If not, throw an error.
+ */
+
+ for ($varToken = ($stackPtr - 1); $varToken >= 0; $varToken--) {
+ // Skip brackets.
+ if (isset($tokens[$varToken]['parenthesis_opener']) === true && $tokens[$varToken]['parenthesis_opener'] < $varToken) {
+ $varToken = $tokens[$varToken]['parenthesis_opener'];
+ continue;
+ }
+
+ if (isset($tokens[$varToken]['bracket_opener']) === true) {
+ $varToken = $tokens[$varToken]['bracket_opener'];
+ continue;
+ }
+
+ if ($tokens[$varToken]['code'] === T_SEMICOLON) {
+ // We've reached the next statement, so we
+ // didn't find a variable.
+ return;
+ }
+
+ if ($tokens[$varToken]['code'] === T_VARIABLE) {
+ // We found our variable.
+ break;
+ }
+ }//end for
+
+ if ($varToken <= 0) {
+ // Didn't find a variable.
+ return;
+ }
+
+ // Deal with this type of variable: self::$var by setting the var
+ // token to be "self" rather than "$var".
+ if ($tokens[($varToken - 1)]['code'] === T_DOUBLE_COLON) {
+ $varToken = ($varToken - 2);
+ }
+
+ // Deal with this type of variable: $obj->$var by setting the var
+ // token to be "$obj" rather than "$var".
+ if ($tokens[($varToken - 1)]['code'] === T_OBJECT_OPERATOR) {
+ $varToken = ($varToken - 2);
+ }
+
+ // Deal with this type of variable: $$var by setting the var
+ // token to be "$" rather than "$var".
+ if ($tokens[($varToken - 1)]['content'] === '$') {
+ $varToken--;
+ }
+
+ // Ignore member var definitions.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($varToken - 1), null, true);
+ if (isset(Tokens::$scopeModifiers[$tokens[$prev]['code']]) === true
+ || $tokens[$prev]['code'] === T_VAR
+ ) {
+ return;
+ }
+
+ if ($tokens[$prev]['code'] === T_STATIC) {
+ return;
+ }
+
+ // Make sure this variable is the first thing in the statement.
+ $varLine = $tokens[$varToken]['line'];
+ $prevLine = 0;
+ for ($i = ($varToken - 1); $i >= 0; $i--) {
+ if ($tokens[$i]['code'] === T_SEMICOLON) {
+ // We reached the end of the statement.
+ return;
+ }
+
+ if ($tokens[$i]['code'] === T_INLINE_THEN) {
+ // We reached the end of the inline THEN statement.
+ return;
+ }
+
+ if ($tokens[$i]['code'] === T_INLINE_ELSE) {
+ // We reached the end of the inline ELSE statement.
+ return;
+ }
+
+ if ($tokens[$i]['code'] === T_OPEN_TAG) {
+ // We reached the end of the code block.
+ return;
+ }
+
+ if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === false) {
+ $prevLine = $tokens[$i]['line'];
+ break;
+ }
+ }//end for
+
+ // Ignore the first part of FOR loops as we are allowed to
+ // assign variables there even though the variable is not the
+ // first thing on the line. Also ignore WHILE loops.
+ if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS && isset($tokens[$i]['parenthesis_owner']) === true) {
+ $owner = $tokens[$i]['parenthesis_owner'];
+ if ($tokens[$owner]['code'] === T_FOR || $tokens[$owner]['code'] === T_WHILE) {
+ return;
+ }
+ }
+
+ if ($prevLine === $varLine) {
+ $error = 'Assignments must be the first block of code on a line';
+ $phpcsFile->addError($error, $stackPtr, 'Found');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the indenting used when an ob_start() call occurs.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DisallowObEndFlushSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_STRING);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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]['content'] === 'ob_end_flush') {
+ $phpcsFile->addError('Use of ob_end_flush() is not allowed; use ob_get_contents() and ob_end_clean() instead', $stackPtr, 'Found');
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Bans the use of size-based functions in loop conditions.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DisallowSizeFunctionsInLoopsSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+ /**
+ * An array of functions we don't want in the condition of loops.
+ *
+ * @var array
+ */
+ protected $forbiddenFunctions = array(
+ 'PHP' => array(
+ 'sizeof' => true,
+ 'strlen' => true,
+ 'count' => true,
+ ),
+ 'JS' => array('length' => true),
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_WHILE,
+ T_FOR,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $tokenizer = $phpcsFile->tokenizerType;
+ $openBracket = $tokens[$stackPtr]['parenthesis_opener'];
+ $closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
+
+ if ($tokens[$stackPtr]['code'] === T_FOR) {
+ // We only want to check the condition in FOR loops.
+ $start = $phpcsFile->findNext(T_SEMICOLON, ($openBracket + 1));
+ $end = $phpcsFile->findPrevious(T_SEMICOLON, ($closeBracket - 1));
+ } else {
+ $start = $openBracket;
+ $end = $closeBracket;
+ }
+
+ for ($i = ($start + 1); $i < $end; $i++) {
+ if ($tokens[$i]['code'] === T_STRING
+ && isset($this->forbiddenFunctions[$tokenizer][$tokens[$i]['content']]) === true
+ ) {
+ $functionName = $tokens[$i]['content'];
+ if ($tokenizer === 'JS') {
+ // Needs to be in the form object.function to be valid.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($i - 1), null, true);
+ if ($prev === false || $tokens[$prev]['code'] !== T_OBJECT_OPERATOR) {
+ continue;
+ }
+
+ $functionName = 'object.'.$functionName;
+ } else {
+ // Make sure it isn't a member var.
+ if ($tokens[($i - 1)]['code'] === T_OBJECT_OPERATOR) {
+ continue;
+ }
+
+ $functionName .= '()';
+ }
+
+ $error = 'The use of %s inside a loop condition is not allowed; assign the return value to a variable and use the variable in the loop condition instead';
+ $data = array($functionName);
+ $phpcsFile->addError($error, $i, 'Found', $data);
+ }//end if
+ }//end for
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Discourages the use of debug functions.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\ForbiddenFunctionsSniff as GenericForbiddenFunctionsSniff;
+
+class DiscouragedFunctionsSniff extends GenericForbiddenFunctionsSniff
+{
+
+ /**
+ * A list of forbidden functions with their alternatives.
+ *
+ * The value is NULL if no alternative exists. IE, the
+ * function should just not be used.
+ *
+ * @var array<string, string|null>
+ */
+ public $forbiddenFunctions = array(
+ 'error_log' => null,
+ 'print_r' => null,
+ 'var_dump' => null,
+ );
+
+ /**
+ * If true, an error will be thrown; otherwise a warning.
+ *
+ * @var boolean
+ */
+ public $error = false;
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the indentation of embedded PHP code segments.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class EmbeddedPhpSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 the close php tag is on the same line as the opening
+ // then we have an inline embedded PHP block.
+ $closeTag = $phpcsFile->findNext(T_CLOSE_TAG, $stackPtr);
+ if ($closeTag === false || $tokens[$stackPtr]['line'] !== $tokens[$closeTag]['line']) {
+ $this->validateMultilineEmbeddedPhp($phpcsFile, $stackPtr);
+ } else {
+ $this->validateInlineEmbeddedPhp($phpcsFile, $stackPtr);
+ }
+
+ }//end process()
+
+
+ /**
+ * Validates embedded PHP that exists on multiple lines.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ private function validateMultilineEmbeddedPhp($phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $prevTag = $phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1));
+ if ($prevTag === false) {
+ // This is the first open tag.
+ return;
+ }
+
+ $firstContent = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ $closingTag = $phpcsFile->findNext(T_CLOSE_TAG, $stackPtr);
+ if ($closingTag !== false) {
+ $nextContent = $phpcsFile->findNext(T_WHITESPACE, ($closingTag + 1), $phpcsFile->numTokens, true);
+ if ($nextContent === false) {
+ // Final closing tag. It will be handled elsewhere.
+ return;
+ }
+
+ // We have an opening and a closing tag, that lie within other content.
+ if ($firstContent === $closingTag) {
+ $error = 'Empty embedded PHP tag found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Empty');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = $stackPtr; $i <= $closingTag; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ return;
+ }
+ }//end if
+
+ if ($tokens[$firstContent]['line'] === $tokens[$stackPtr]['line']) {
+ $error = 'Opening PHP tag must be on a line by itself';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'ContentAfterOpen');
+ if ($fix === true) {
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $stackPtr, true);
+ $padding = (strlen($tokens[$first]['content']) - strlen(ltrim($tokens[$first]['content'])));
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->addNewline($stackPtr);
+ $phpcsFile->fixer->addContent($stackPtr, str_repeat(' ', $padding));
+ $phpcsFile->fixer->endChangeset();
+ }
+ } else {
+ // Check the indent of the first line, except if it is a scope closer.
+ if (isset($tokens[$firstContent]['scope_closer']) === false
+ || $tokens[$firstContent]['scope_closer'] !== $firstContent
+ ) {
+ // Check for a blank line at the top.
+ if ($tokens[$firstContent]['line'] > ($tokens[$stackPtr]['line'] + 1)) {
+ // Find a token on the blank line to throw the error on.
+ $i = $stackPtr;
+ do {
+ $i++;
+ } while ($tokens[$i]['line'] !== ($tokens[$stackPtr]['line'] + 1));
+
+ $error = 'Blank line found at start of embedded PHP content';
+ $fix = $phpcsFile->addFixableError($error, $i, 'SpacingBefore');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($stackPtr + 1); $i < $firstContent; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$firstContent]['line']
+ || $tokens[$i]['line'] === $tokens[$stackPtr]['line']
+ ) {
+ continue;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }//end if
+
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $stackPtr);
+ if ($first === false) {
+ $first = $phpcsFile->findFirstOnLine(T_INLINE_HTML, $stackPtr);
+ $indent = (strlen($tokens[$first]['content']) - strlen(ltrim($tokens[$first]['content'])));
+ } else {
+ $indent = ($tokens[($first + 1)]['column'] - 1);
+ }
+
+ $contentColumn = ($tokens[$firstContent]['column'] - 1);
+ if ($contentColumn !== $indent) {
+ $error = 'First line of embedded PHP code must be indented %s spaces; %s found';
+ $data = array(
+ $indent,
+ $contentColumn,
+ );
+ $fix = $phpcsFile->addFixableError($error, $firstContent, 'Indent', $data);
+ if ($fix === true) {
+ $padding = str_repeat(' ', $indent);
+ if ($contentColumn === 0) {
+ $phpcsFile->fixer->addContentBefore($firstContent, $padding);
+ } else {
+ $phpcsFile->fixer->replaceToken(($firstContent - 1), $padding);
+ }
+ }
+ }
+ }//end if
+ }//end if
+
+ $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($tokens[$lastContent]['line'] === $tokens[$stackPtr]['line']
+ && trim($tokens[$lastContent]['content']) !== ''
+ ) {
+ $error = 'Opening PHP tag must be on a line by itself';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'ContentBeforeOpen');
+ if ($fix === true) {
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $stackPtr);
+ if ($first === false) {
+ $first = $phpcsFile->findFirstOnLine(T_INLINE_HTML, $stackPtr);
+ $padding = (strlen($tokens[$first]['content']) - strlen(ltrim($tokens[$first]['content'])));
+ } else {
+ $padding = ($tokens[($first + 1)]['column'] - 1);
+ }
+
+ $phpcsFile->fixer->addContentBefore($stackPtr, $phpcsFile->eolChar.str_repeat(' ', $padding));
+ }
+ } else {
+ // Find the first token on the first non-empty line we find.
+ for ($first = ($stackPtr - 1); $first > 0; $first--) {
+ if ($tokens[$first]['line'] === $tokens[$stackPtr]['line']) {
+ continue;
+ } else if (trim($tokens[$first]['content']) !== '') {
+ $first = $phpcsFile->findFirstOnLine(array(), $first, true);
+ break;
+ }
+ }
+
+ $expected = 0;
+ if ($tokens[$first]['code'] === T_INLINE_HTML
+ && trim($tokens[$first]['content']) !== ''
+ ) {
+ $expected = (strlen($tokens[$first]['content']) - strlen(ltrim($tokens[$first]['content'])));
+ } else if ($tokens[$first]['code'] === T_WHITESPACE) {
+ $expected = ($tokens[($first + 1)]['column'] - 1);
+ }
+
+ $expected += 4;
+ $found = ($tokens[$stackPtr]['column'] - 1);
+ if ($found > $expected) {
+ $error = 'Opening PHP tag indent incorrect; expected no more than %s spaces but found %s';
+ $data = array(
+ $expected,
+ $found,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'OpenTagIndent', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), str_repeat(' ', $expected));
+ }
+ }
+ }//end if
+
+ if ($closingTag === false) {
+ return;
+ }
+
+ $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($closingTag - 1), ($stackPtr + 1), true);
+ $nextContent = $phpcsFile->findNext(T_WHITESPACE, ($closingTag + 1), null, true);
+
+ if ($tokens[$lastContent]['line'] === $tokens[$closingTag]['line']) {
+ $error = 'Closing PHP tag must be on a line by itself';
+ $fix = $phpcsFile->addFixableError($error, $closingTag, 'ContentBeforeEnd');
+ if ($fix === true) {
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $closingTag, true);
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->addContentBefore($closingTag, str_repeat(' ', ($tokens[$first]['column'] - 1)));
+ $phpcsFile->fixer->addNewlineBefore($closingTag);
+ $phpcsFile->fixer->endChangeset();
+ }
+ } else if ($tokens[$nextContent]['line'] === $tokens[$closingTag]['line']) {
+ $error = 'Closing PHP tag must be on a line by itself';
+ $fix = $phpcsFile->addFixableError($error, $closingTag, 'ContentAfterEnd');
+ if ($fix === true) {
+ $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $closingTag, true);
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->addNewline($closingTag);
+ $phpcsFile->fixer->addContent($closingTag, str_repeat(' ', ($tokens[$first]['column'] - 1)));
+ $phpcsFile->fixer->endChangeset();
+ }
+ }//end if
+
+ $next = $phpcsFile->findNext(T_OPEN_TAG, ($closingTag + 1));
+ if ($next === false) {
+ return;
+ }
+
+ // Check for a blank line at the bottom.
+ if ((isset($tokens[$lastContent]['scope_closer']) === false
+ || $tokens[$lastContent]['scope_closer'] !== $lastContent)
+ && $tokens[$lastContent]['line'] < ($tokens[$closingTag]['line'] - 1)
+ ) {
+ // Find a token on the blank line to throw the error on.
+ $i = $closingTag;
+ do {
+ $i--;
+ } while ($tokens[$i]['line'] !== ($tokens[$closingTag]['line'] - 1));
+
+ $error = 'Blank line found at end of embedded PHP content';
+ $fix = $phpcsFile->addFixableError($error, $i, 'SpacingAfter');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($lastContent + 1); $i < $closingTag; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$lastContent]['line']
+ || $tokens[$i]['line'] === $tokens[$closingTag]['line']
+ ) {
+ continue;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }//end if
+
+ }//end validateMultilineEmbeddedPhp()
+
+
+ /**
+ * Validates embedded PHP that exists on one line.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ private function validateInlineEmbeddedPhp($phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // We only want one line PHP sections, so return if the closing tag is
+ // on the next line.
+ $closeTag = $phpcsFile->findNext(T_CLOSE_TAG, $stackPtr, null, false);
+ if ($tokens[$stackPtr]['line'] !== $tokens[$closeTag]['line']) {
+ return;
+ }
+
+ // Check that there is one, and only one space at the start of the statement.
+ $firstContent = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), $closeTag, true);
+
+ if ($firstContent === false) {
+ $error = 'Empty embedded PHP tag found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Empty');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = $stackPtr; $i <= $closeTag; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ return;
+ }
+
+ // The open tag token always contains a single space after it.
+ $leadingSpace = 1;
+ if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
+ $leadingSpace = (strlen($tokens[($stackPtr + 1)]['content']) + 1);
+ }
+
+ if ($leadingSpace !== 1) {
+ $error = 'Expected 1 space after opening PHP tag; %s found';
+ $data = array($leadingSpace);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterOpen', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), '');
+ }
+ }
+
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($closeTag - 1), $stackPtr, true);
+ if ($prev !== $stackPtr) {
+ if ((isset($tokens[$prev]['scope_opener']) === false
+ || $tokens[$prev]['scope_opener'] !== $prev)
+ && (isset($tokens[$prev]['scope_closer']) === false
+ || $tokens[$prev]['scope_closer'] !== $prev)
+ && $tokens[$prev]['code'] !== T_SEMICOLON
+ ) {
+ $error = 'Inline PHP statement must end with a semicolon';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSemicolon');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($prev, ';');
+ }
+ } else if ($tokens[$prev]['code'] === T_SEMICOLON) {
+ $statementCount = 1;
+ for ($i = ($stackPtr + 1); $i < $prev; $i++) {
+ if ($tokens[$i]['code'] === T_SEMICOLON) {
+ $statementCount++;
+ }
+ }
+
+ if ($statementCount > 1) {
+ $error = 'Inline PHP statement must contain a single statement; %s found';
+ $data = array($statementCount);
+ $phpcsFile->addError($error, $stackPtr, 'MultipleStatements', $data);
+ }
+ }
+ }//end if
+
+ $trailingSpace = 0;
+ if ($tokens[($closeTag - 1)]['code'] === T_WHITESPACE) {
+ $trailingSpace = strlen($tokens[($closeTag - 1)]['content']);
+ } else if ($tokens[($closeTag - 1)]['code'] === T_COMMENT
+ && substr($tokens[($closeTag - 1)]['content'], -1) === ' '
+ ) {
+ $trailingSpace = (strlen($tokens[($closeTag - 1)]['content']) - strlen(rtrim($tokens[($closeTag - 1)]['content'])));
+ }
+
+ if ($trailingSpace !== 1) {
+ $error = 'Expected 1 space before closing PHP tag; %s found';
+ $data = array($trailingSpace);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeClose', $data);
+ if ($fix === true) {
+ if ($trailingSpace === 0) {
+ $phpcsFile->fixer->addContentBefore($closeTag, ' ');
+ } else if ($tokens[($closeTag - 1)]['code'] === T_COMMENT) {
+ $phpcsFile->fixer->replaceToken(($closeTag - 1), rtrim($tokens[($closeTag - 1)]['content']).' ');
+ } else {
+ $phpcsFile->fixer->replaceToken(($closeTag - 1), ' ');
+ }
+ }
+ }
+
+ }//end validateInlineEmbeddedPhp()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * The use of eval() is discouraged.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class EvalSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_EVAL);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $error = 'Use of eval() is discouraged';
+ $phpcsFile->addWarning($error, $stackPtr, 'Discouraged');
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Discourages the use of alias functions.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\ForbiddenFunctionsSniff as GenericForbiddenFunctionsSniff;
+
+class ForbiddenFunctionsSniff extends GenericForbiddenFunctionsSniff
+{
+
+ /**
+ * A list of forbidden functions with their alternatives.
+ *
+ * The value is NULL if no alternative exists. IE, the
+ * function should just not be used.
+ *
+ * @var array<string, string|null>
+ */
+ public $forbiddenFunctions = array(
+ 'sizeof' => 'count',
+ 'delete' => 'unset',
+ 'print' => 'echo',
+ 'is_null' => null,
+ 'create_function' => null,
+ );
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Stops the usage of the "global" keyword.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class GlobalKeywordSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_GLOBAL);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $nextVar = $tokens[$phpcsFile->findNext(array(T_VARIABLE), $stackPtr)];
+ $varName = str_replace('$', '', $nextVar['content']);
+ $error = 'Use of the "global" keyword is forbidden; use "$GLOBALS[\'%s\']" instead';
+ $data = array($varName);
+ $phpcsFile->addError($error, $stackPtr, 'NotAllowed', $data);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Bans the use of heredocs and nowdocs.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class HeredocSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_START_HEREDOC,
+ T_START_NOWDOC,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $error = 'Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead';
+ $phpcsFile->addError($error, $stackPtr, 'NotAllowed');
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that functions within functions are never used.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class InnerFunctionsSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_FUNCTION);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $function = $phpcsFile->getCondition($stackPtr, T_FUNCTION);
+ if ($function === false) {
+ // Not a nested function.
+ return;
+ }
+
+ $class = $phpcsFile->getCondition($stackPtr, T_ANON_CLASS);
+ if ($class !== false && $class > $function) {
+ // Ignore methods in anon classes.
+ return;
+ }
+
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] === T_EQUAL) {
+ // Ignore closures.
+ return;
+ }
+
+ $error = 'The use of inner functions is forbidden';
+ $phpcsFile->addError($error, $stackPtr, 'NotAllowed');
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures all calls to inbuilt PHP functions are lowercase.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class LowercasePHPFunctionsSniff implements Sniff
+{
+
+ /**
+ * String -> int hash map of all php built in function names
+ *
+ * @var array
+ */
+ private $builtInFunctions;
+
+
+ /**
+ * Construct the LowercasePHPFunctionSniff
+ */
+ public function __construct()
+ {
+
+ $allFunctions = get_defined_functions();
+ $this->builtInFunctions = array_flip($allFunctions['internal']);
+
+ }//end __construct()
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_STRING);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ // Make sure this is a function call.
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($next === false) {
+ // Not a function call.
+ return;
+ }
+
+ if ($tokens[$next]['code'] !== T_OPEN_PARENTHESIS) {
+ // Not a function call.
+ return;
+ }
+
+ $prev = $phpcsFile->findPrevious(array(T_WHITESPACE, T_BITWISE_AND), ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] === T_FUNCTION) {
+ // Function declaration, not a function call.
+ return;
+ }
+
+ if ($tokens[$prev]['code'] === T_NS_SEPARATOR) {
+ // Namespaced class/function, not an inbuilt function.
+ return;
+ }
+
+ if ($tokens[$prev]['code'] === T_NEW) {
+ // Object creation, not an inbuilt function.
+ return;
+ }
+
+ if ($tokens[$prev]['code'] === T_OBJECT_OPERATOR) {
+ // Not an inbuilt function.
+ return;
+ }
+
+ if ($tokens[$prev]['code'] === T_DOUBLE_COLON) {
+ // Not an inbuilt function.
+ return;
+ }
+
+ // Make sure it is an inbuilt PHP function.
+ // PHP_CodeSniffer can possibly include user defined functions
+ // through the use of vendor/autoload.php.
+ $content = $tokens[$stackPtr]['content'];
+ if (isset($this->builtInFunctions[strtolower($content)]) === false) {
+ return;
+ }
+
+ if ($content !== strtolower($content)) {
+ $error = 'Calls to inbuilt PHP functions must be lowercase; expected "%s" but found "%s"';
+ $data = array(
+ strtolower($content),
+ $content,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'CallUppercase', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, strtolower($content));
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Warns about code that can never been executed.
+ *
+ * @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\Standards\Squiz\Sniffs\PHP;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class NonExecutableCodeSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_BREAK,
+ T_CONTINUE,
+ T_RETURN,
+ T_THROW,
+ T_EXIT,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 this token is preceded with an "or", it only relates to one line
+ // and should be ignored. For example: fopen() or die().
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] === T_LOGICAL_OR || $tokens[$prev]['code'] === T_BOOLEAN_OR) {
+ return;
+ }
+
+ // Check if this token is actually part of a one-line IF or ELSE statement.
+ for ($i = ($stackPtr - 1); $i > 0; $i--) {
+ if ($tokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
+ $i = $tokens[$i]['parenthesis_opener'];
+ continue;
+ } else if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
+ continue;
+ }
+
+ break;
+ }
+
+ if ($tokens[$i]['code'] === T_IF
+ || $tokens[$i]['code'] === T_ELSE
+ || $tokens[$i]['code'] === T_ELSEIF
+ ) {
+ return;
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_RETURN) {
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$next]['code'] === T_SEMICOLON) {
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($next + 1), null, true);
+ if ($tokens[$next]['code'] === T_CLOSE_CURLY_BRACKET) {
+ // If this is the closing brace of a function
+ // then this return statement doesn't return anything
+ // and is not required anyway.
+ $owner = $tokens[$next]['scope_condition'];
+ if ($tokens[$owner]['code'] === T_FUNCTION) {
+ $warning = 'Empty return statement not required here';
+ $phpcsFile->addWarning($warning, $stackPtr, 'ReturnNotRequired');
+ return;
+ }
+ }
+ }
+ }
+
+ if (isset($tokens[$stackPtr]['scope_opener']) === true) {
+ $owner = $tokens[$stackPtr]['scope_condition'];
+ if ($tokens[$owner]['code'] === T_CASE || $tokens[$owner]['code'] === T_DEFAULT) {
+ // This token closes the scope of a CASE or DEFAULT statement
+ // so any code between this statement and the next CASE, DEFAULT or
+ // end of SWITCH token will not be executable.
+ $end = $phpcsFile->findEndOfStatement($stackPtr);
+ $next = $phpcsFile->findNext(
+ array(
+ T_CASE,
+ T_DEFAULT,
+ T_CLOSE_CURLY_BRACKET,
+ ),
+ ($end + 1)
+ );
+
+ if ($next !== false) {
+ $lastLine = $tokens[$end]['line'];
+ for ($i = ($stackPtr + 1); $i < $next; $i++) {
+ if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
+ continue;
+ }
+
+ $line = $tokens[$i]['line'];
+ if ($line > $lastLine) {
+ $type = substr($tokens[$stackPtr]['type'], 2);
+ $warning = 'Code after %s statement cannot be executed';
+ $data = array($type);
+ $phpcsFile->addWarning($warning, $i, 'Unreachable', $data);
+ $lastLine = $line;
+ }
+ }
+ }//end if
+
+ // That's all we have to check for these types of statements.
+ return;
+ }//end if
+ }//end if
+
+ // This token may be part of an inline condition.
+ // If we find a closing parenthesis that belongs to a condition
+ // we should ignore this token.
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if (isset($tokens[$prev]['parenthesis_owner']) === true) {
+ $owner = $tokens[$prev]['parenthesis_owner'];
+ $ignore = array(
+ T_IF => true,
+ T_ELSE => true,
+ T_ELSEIF => true,
+ );
+ if (isset($ignore[$tokens[$owner]['code']]) === true) {
+ return;
+ }
+ }
+
+ $ourConditions = array_keys($tokens[$stackPtr]['conditions']);
+
+ if (empty($ourConditions) === false) {
+ $condition = array_pop($ourConditions);
+
+ if (isset($tokens[$condition]['scope_closer']) === false) {
+ return;
+ }
+
+ // Special case for BREAK statements sitting directly inside SWITCH
+ // statements. If we get to this point, we know the BREAK is not being
+ // used to close a CASE statement, so it is most likely non-executable
+ // code itself (as is the case when you put return; break; at the end of
+ // a case). So we need to ignore this token.
+ if ($tokens[$condition]['code'] === T_SWITCH
+ && $tokens[$stackPtr]['code'] === T_BREAK
+ ) {
+ return;
+ }
+
+ $closer = $tokens[$condition]['scope_closer'];
+
+ // If the closer for our condition is shared with other openers,
+ // we will need to throw errors from this token to the next
+ // shared opener (if there is one), not to the scope closer.
+ $nextOpener = null;
+ for ($i = ($stackPtr + 1); $i < $closer; $i++) {
+ if (isset($tokens[$i]['scope_closer']) === true) {
+ if ($tokens[$i]['scope_closer'] === $closer) {
+ // We found an opener that shares the same
+ // closing token as us.
+ $nextOpener = $i;
+ break;
+ }
+ }
+ }//end for
+
+ if ($nextOpener === null) {
+ $end = $closer;
+ } else {
+ $end = ($nextOpener - 1);
+ }
+ } else {
+ // This token is in the global scope.
+ if ($tokens[$stackPtr]['code'] === T_BREAK) {
+ return;
+ }
+
+ // Throw an error for all lines until the end of the file.
+ $end = ($phpcsFile->numTokens - 1);
+ }//end if
+
+ // Find the semicolon that ends this statement, skipping
+ // nested statements like FOR loops and closures.
+ for ($start = ($stackPtr + 1); $start < $phpcsFile->numTokens; $start++) {
+ if ($start === $end) {
+ break;
+ }
+
+ if ($tokens[$start]['code'] === T_OPEN_PARENTHESIS) {
+ $start = $tokens[$start]['parenthesis_closer'];
+ continue;
+ }
+
+ if ($tokens[$start]['code'] === T_OPEN_CURLY_BRACKET) {
+ $start = $tokens[$start]['bracket_closer'];
+ continue;
+ }
+
+ if ($tokens[$start]['code'] === T_SEMICOLON) {
+ break;
+ }
+ }//end for
+
+ $lastLine = $tokens[$start]['line'];
+ for ($i = ($start + 1); $i < $end; $i++) {
+ if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true
+ || isset(Tokens::$bracketTokens[$tokens[$i]['code']]) === true
+ ) {
+ continue;
+ }
+
+ // Skip whole functions and classes/interfaces because they are not
+ // technically executed code, but rather declarations that may be used.
+ if ($tokens[$i]['code'] === T_FUNCTION
+ || $tokens[$i]['code'] === T_CLASS
+ || $tokens[$i]['code'] === T_INTERFACE
+ ) {
+ $i = $tokens[$i]['scope_closer'];
+ continue;
+ }
+
+ $line = $tokens[$i]['line'];
+ if ($line > $lastLine) {
+ $type = substr($tokens[$stackPtr]['type'], 2);
+ $warning = 'Code after %s statement cannot be executed';
+ $data = array($type);
+ $phpcsFile->addWarning($warning, $i, 'Unreachable', $data);
+ $lastLine = $line;
+ }
+ }//end for
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that class members have scope modifiers.
+ *
+ * @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\Standards\Squiz\Sniffs\Scope;
+
+use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class MemberVarScopeSniff extends AbstractVariableSniff
+{
+
+
+ /**
+ * Processes the function tokens within the class.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position where the token was found.
+ *
+ * @return void
+ */
+ protected function processMemberVar(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $modifier = null;
+ for ($i = ($stackPtr - 1); $i > 0; $i--) {
+ if ($tokens[$i]['line'] < $tokens[$stackPtr]['line']) {
+ break;
+ } else if (isset(Tokens::$scopeModifiers[$tokens[$i]['code']]) === true) {
+ $modifier = $i;
+ break;
+ }
+ }
+
+ if ($modifier === null) {
+ $error = 'Scope modifier not specified for member variable "%s"';
+ $data = array($tokens[$stackPtr]['content']);
+ $phpcsFile->addError($error, $stackPtr, 'Missing', $data);
+ }
+
+ }//end processMemberVar()
+
+
+ /**
+ * Processes normal variables.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position where the token was found.
+ *
+ * @return void
+ */
+ protected function processVariable(File $phpcsFile, $stackPtr)
+ {
+ /*
+ We don't care about normal variables.
+ */
+
+ }//end processVariable()
+
+
+ /**
+ * Processes variables in double quoted strings.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position where the token was found.
+ *
+ * @return void
+ */
+ protected function processVariableInString(File $phpcsFile, $stackPtr)
+ {
+ /*
+ We don't care about normal variables.
+ */
+
+ }//end processVariableInString()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that class methods have scope modifiers.
+ *
+ * @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\Standards\Squiz\Sniffs\Scope;
+
+use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class MethodScopeSniff extends AbstractScopeSniff
+{
+
+
+ /**
+ * Constructs a Squiz_Sniffs_Scope_MethodScopeSniff.
+ */
+ public function __construct()
+ {
+ parent::__construct(array(T_CLASS, T_INTERFACE, T_TRAIT), array(T_FUNCTION));
+
+ }//end __construct()
+
+
+ /**
+ * Processes the function tokens within the class.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The 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
+ */
+ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $methodName = $phpcsFile->getDeclarationName($stackPtr);
+ if ($methodName === null) {
+ // Ignore closures.
+ return;
+ }
+
+ if ($phpcsFile->hasCondition($stackPtr, T_FUNCTION) === true) {
+ // Ignore nested functions.
+ return;
+ }
+
+ $modifier = null;
+ for ($i = ($stackPtr - 1); $i > 0; $i--) {
+ if ($tokens[$i]['line'] < $tokens[$stackPtr]['line']) {
+ break;
+ } else if (isset(Tokens::$scopeModifiers[$tokens[$i]['code']]) === true) {
+ $modifier = $i;
+ break;
+ }
+ }
+
+ if ($modifier === null) {
+ $error = 'Visibility must be declared on method "%s"';
+ $data = array($methodName);
+ $phpcsFile->addError($error, $stackPtr, 'Missing', $data);
+ }
+
+ }//end processTokenWithinScope()
+
+
+ /**
+ * 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.
+ *
+ * @return void
+ */
+ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
+ {
+
+ }//end processTokenOutsideScope()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks for usage of $this in static methods, which will cause runtime errors.
+ *
+ * @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\Standards\Squiz\Sniffs\Scope;
+
+use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
+use PHP_CodeSniffer\Files\File;
+
+class StaticThisUsageSniff extends AbstractScopeSniff
+{
+
+
+ /**
+ * Constructs the test with the tokens it wishes to listen for.
+ */
+ public function __construct()
+ {
+ parent::__construct(array(T_CLASS), array(T_FUNCTION));
+
+ }//end __construct()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ * @param int $currScope A pointer to the start of the scope.
+ *
+ * @return void
+ */
+ public function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $function = $tokens[($stackPtr + 2)];
+
+ if ($function['code'] !== T_STRING) {
+ return;
+ }
+
+ $functionName = $function['content'];
+ $classOpener = $tokens[$currScope]['scope_condition'];
+ $className = $tokens[($classOpener + 2)]['content'];
+
+ $methodProps = $phpcsFile->getMethodProperties($stackPtr);
+
+ if ($methodProps['is_static'] === true) {
+ if (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ // There is no scope opener or closer, so the function
+ // must be abstract.
+ return;
+ }
+
+ $thisUsage = $stackPtr;
+ while (($thisUsage = $phpcsFile->findNext(array(T_VARIABLE), ($thisUsage + 1), $tokens[$stackPtr]['scope_closer'], false, '$this')) !== false) {
+ if ($thisUsage === false) {
+ return;
+ }
+
+ $error = 'Usage of "$this" in static methods will cause runtime errors';
+ $phpcsFile->addError($error, $thisUsage, 'Found');
+ }
+ }//end if
+
+ }//end processTokenWithinScope()
+
+
+ /**
+ * 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.
+ *
+ * @return void
+ */
+ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
+ {
+
+ }//end processTokenOutsideScope()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Makes sure there are no spaces around the concatenation operator.
+ *
+ * @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\Standards\Squiz\Sniffs\Strings;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ConcatenationSpacingSniff implements Sniff
+{
+
+ /**
+ * The number of spaces before and after a string concat.
+ *
+ * @var integer
+ */
+ public $spacing = 0;
+
+ /**
+ * Allow newlines instead of spaces.
+ *
+ * @var boolean
+ */
+ public $ignoreNewlines = false;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_STRING_CONCAT);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $ignoreBefore = false;
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] === T_END_HEREDOC || $tokens[$prev]['code'] === T_END_NOWDOC) {
+ // Spacing before must be preserved due to the here/nowdoc closing tag.
+ $ignoreBefore = true;
+ }
+
+ $this->spacing = (int) $this->spacing;
+
+ if ($ignoreBefore === false) {
+ if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE) {
+ $before = 0;
+ } else {
+ if ($tokens[($stackPtr - 2)]['line'] !== $tokens[$stackPtr]['line']) {
+ $before = 'newline';
+ } else {
+ $before = $tokens[($stackPtr - 1)]['length'];
+ }
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Spacing before string concat', $before);
+ }
+
+ if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
+ $after = 0;
+ } else {
+ if ($tokens[($stackPtr + 2)]['line'] !== $tokens[$stackPtr]['line']) {
+ $after = 'newline';
+ } else {
+ $after = $tokens[($stackPtr + 1)]['length'];
+ }
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Spacing after string concat', $after);
+
+ if (($ignoreBefore === true
+ || $before === $this->spacing
+ || ($before === 'newline'
+ && $this->ignoreNewlines === true))
+ && ($after === $this->spacing
+ || ($after === 'newline'
+ && $this->ignoreNewlines === true))
+ ) {
+ return;
+ }
+
+ if ($this->spacing === 0) {
+ $message = 'Concat operator must not be surrounded by spaces';
+ $data = array();
+ } else {
+ if ($this->spacing > 1) {
+ $message = 'Concat operator must be surrounded by %s spaces';
+ } else {
+ $message = 'Concat operator must be surrounded by a single space';
+ }
+
+ $data = array($this->spacing);
+ }
+
+ $fix = $phpcsFile->addFixableError($message, $stackPtr, 'PaddingFound', $data);
+
+ if ($fix === true) {
+ $padding = str_repeat(' ', $this->spacing);
+ if ($ignoreBefore === false && ($before !== 'newline' || $this->ignoreNewlines === false)) {
+ if ($tokens[($stackPtr - 1)]['code'] === T_WHITESPACE) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), $padding);
+ if ($this->spacing === 0
+ && ($tokens[($stackPtr - 2)]['code'] === T_LNUMBER
+ || $tokens[($stackPtr - 2)]['code'] === T_DNUMBER)
+ ) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 2), '('.$tokens[($stackPtr - 2)]['content'].')');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ } else if ($this->spacing > 0) {
+ $phpcsFile->fixer->addContent(($stackPtr - 1), $padding);
+ }
+ }
+
+ if ($after !== 'newline' || $this->ignoreNewlines === false) {
+ if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), $padding);
+ if ($this->spacing === 0
+ && ($tokens[($stackPtr + 2)]['code'] === T_LNUMBER
+ || $tokens[($stackPtr + 2)]['code'] === T_DNUMBER)
+ ) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 2), '('.$tokens[($stackPtr + 2)]['content'].')');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ } else if ($this->spacing > 0) {
+ $phpcsFile->fixer->addContent($stackPtr, $padding);
+ }
+ }
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Makes sure that any use of double quotes strings are warranted.
+ *
+ * @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\Standards\Squiz\Sniffs\Strings;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class DoubleQuoteUsageSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_CONSTANT_ENCAPSED_STRING,
+ T_DOUBLE_QUOTED_STRING,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 tabs are being converted to spaces by the tokeniser, the
+ // original content should be used instead of the converted content.
+ if (isset($tokens[$stackPtr]['orig_content']) === true) {
+ $workingString = $tokens[$stackPtr]['orig_content'];
+ } else {
+ $workingString = $tokens[$stackPtr]['content'];
+ }
+
+ $lastStringToken = $stackPtr;
+
+ $i = ($stackPtr + 1);
+ if (isset($tokens[$i]) === true) {
+ while ($i < $phpcsFile->numTokens
+ && $tokens[$i]['code'] === $tokens[$stackPtr]['code']
+ ) {
+ if (isset($tokens[$i]['orig_content']) === true) {
+ $workingString .= $tokens[$i]['orig_content'];
+ } else {
+ $workingString .= $tokens[$i]['content'];
+ }
+
+ $lastStringToken = $i;
+ $i++;
+ }
+ }
+
+ $skipTo = ($lastStringToken + 1);
+
+ // Check if it's a double quoted string.
+ if ($workingString[0] !== '"' || substr($workingString, -1) !== '"') {
+ return $skipTo;
+ }
+
+ // The use of variables in double quoted strings is not allowed.
+ if ($tokens[$stackPtr]['code'] === T_DOUBLE_QUOTED_STRING) {
+ $stringTokens = token_get_all('<?php '.$workingString);
+ foreach ($stringTokens as $token) {
+ if (is_array($token) === true && $token[0] === T_VARIABLE) {
+ $error = 'Variable "%s" not allowed in double quoted string; use concatenation instead';
+ $data = array($token[1]);
+ $phpcsFile->addError($error, $stackPtr, 'ContainsVar', $data);
+ }
+ }
+
+ return $skipTo;
+ }//end if
+
+ $allowedChars = array(
+ '\0',
+ '\1',
+ '\2',
+ '\3',
+ '\4',
+ '\5',
+ '\6',
+ '\7',
+ '\n',
+ '\r',
+ '\f',
+ '\t',
+ '\v',
+ '\x',
+ '\b',
+ '\e',
+ '\u',
+ '\'',
+ );
+
+ foreach ($allowedChars as $testChar) {
+ if (strpos($workingString, $testChar) !== false) {
+ return $skipTo;
+ }
+ }
+
+ $error = 'String %s does not require double quotes; use single quotes instead';
+ $data = array(str_replace(array("\r", "\n"), array('\r', '\n'), $workingString));
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotRequired', $data);
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $innerContent = substr($workingString, 1, -1);
+ $innerContent = str_replace('\"', '"', $innerContent);
+ $innerContent = str_replace('\\$', '$', $innerContent);
+ $phpcsFile->fixer->replaceToken($stackPtr, "'$innerContent'");
+ while ($lastStringToken !== $stackPtr) {
+ $phpcsFile->fixer->replaceToken($lastStringToken, '');
+ $lastStringToken--;
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ return $skipTo;
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Makes sure that any strings that are "echoed" are not enclosed in brackets.
+ *
+ * @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\Standards\Squiz\Sniffs\Strings;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class EchoedStringsSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_ECHO);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $firstContent = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ // If the first non-whitespace token is not an opening parenthesis, then we are not concerned.
+ if ($tokens[$firstContent]['code'] !== T_OPEN_PARENTHESIS) {
+ $phpcsFile->recordMetric($stackPtr, 'Brackets around echoed strings', 'no');
+ return;
+ }
+
+ $end = $phpcsFile->findNext(array(T_SEMICOLON, T_CLOSE_TAG), $stackPtr, null, false);
+
+ // If the token before the semi-colon is not a closing parenthesis, then we are not concerned.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($end - 1), null, true);
+ if ($tokens[$prev]['code'] !== T_CLOSE_PARENTHESIS) {
+ $phpcsFile->recordMetric($stackPtr, 'Brackets around echoed strings', 'no');
+ return;
+ }
+
+ // If the parenthesis don't match, then we are not concerned.
+ if ($tokens[$firstContent]['parenthesis_closer'] !== $prev) {
+ $phpcsFile->recordMetric($stackPtr, 'Brackets around echoed strings', 'no');
+ return;
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Brackets around echoed strings', 'yes');
+
+ if (($phpcsFile->findNext(Tokens::$operators, $stackPtr, $end, false)) === false) {
+ // There are no arithmetic operators in this.
+ $error = 'Echoed strings should not be bracketed';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'HasBracket');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken($firstContent, '');
+ if ($tokens[($firstContent - 1)]['code'] !== T_WHITESPACE) {
+ $phpcsFile->fixer->addContent(($firstContent - 1), ' ');
+ }
+
+ $phpcsFile->fixer->replaceToken($prev, '');
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure cast statements don't contain whitespace.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class CastSpacingSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return Tokens::$castTokens;
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $content = $tokens[$stackPtr]['content'];
+ $expected = str_replace(' ', '', $content);
+ $expected = str_replace("\t", '', $expected);
+
+ if ($content !== $expected) {
+ $error = 'Cast statements must not contain whitespace; expected "%s" but found "%s"';
+ $data = array(
+ $expected,
+ $content,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'ContainsWhiteSpace', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, $expected);
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that control structures have the correct spacing around brackets.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ControlStructureSpacingSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_IF,
+ T_WHILE,
+ T_FOREACH,
+ T_FOR,
+ T_SWITCH,
+ T_DO,
+ T_ELSE,
+ T_ELSEIF,
+ T_TRY,
+ T_CATCH,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[$stackPtr]['parenthesis_opener']) === true
+ && isset($tokens[$stackPtr]['parenthesis_closer']) === true
+ ) {
+ $parenOpener = $tokens[$stackPtr]['parenthesis_opener'];
+ $parenCloser = $tokens[$stackPtr]['parenthesis_closer'];
+ if ($tokens[($parenOpener + 1)]['code'] === T_WHITESPACE) {
+ $gap = $tokens[($parenOpener + 1)]['length'];
+
+ if ($gap === 0) {
+ $phpcsFile->recordMetric($stackPtr, 'Spaces after control structure open parenthesis', 'newline');
+ $gap = 'newline';
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Spaces after control structure open parenthesis', $gap);
+ }
+
+ $error = 'Expected 0 spaces after opening bracket; %s found';
+ $data = array($gap);
+ $fix = $phpcsFile->addFixableError($error, ($parenOpener + 1), 'SpacingAfterOpenBrace', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($parenOpener + 1), '');
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Spaces after control structure open parenthesis', 0);
+ }
+
+ if ($tokens[$parenOpener]['line'] === $tokens[$parenCloser]['line']
+ && $tokens[($parenCloser - 1)]['code'] === T_WHITESPACE
+ ) {
+ $gap = $tokens[($parenCloser - 1)]['length'];
+ $error = 'Expected 0 spaces before closing bracket; %s found';
+ $data = array($gap);
+ $fix = $phpcsFile->addFixableError($error, ($parenCloser - 1), 'SpaceBeforeCloseBrace', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($parenCloser - 1), '');
+ }
+
+ if ($gap === 0) {
+ $phpcsFile->recordMetric($stackPtr, 'Spaces before control structure close parenthesis', 'newline');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Spaces before control structure close parenthesis', $gap);
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Spaces before control structure close parenthesis', 0);
+ }
+ }//end if
+
+ if (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ return;
+ }
+
+ $scopeOpener = $tokens[$stackPtr]['scope_opener'];
+ $scopeCloser = $tokens[$stackPtr]['scope_closer'];
+
+ for ($firstContent = ($scopeOpener + 1); $firstContent < $phpcsFile->numTokens; $firstContent++) {
+ $code = $tokens[$firstContent]['code'];
+
+ if ($code === T_WHITESPACE
+ || ($code === T_INLINE_HTML
+ && trim($tokens[$firstContent]['content']) === '')
+ ) {
+ continue;
+ }
+
+ // Skip all empty tokens on the same line as the opener.
+ if ($tokens[$firstContent]['line'] === $tokens[$scopeOpener]['line']
+ && (isset(Tokens::$emptyTokens[$code]) === true
+ || $code === T_CLOSE_TAG)
+ ) {
+ continue;
+ }
+
+ break;
+ }
+
+ // We ignore spacing for some structures that tend to have their own rules.
+ $ignore = array(
+ T_FUNCTION => true,
+ T_CLASS => true,
+ T_INTERFACE => true,
+ T_TRAIT => true,
+ T_DOC_COMMENT_OPEN_TAG => true,
+ );
+
+ if (isset($ignore[$tokens[$firstContent]['code']]) === false
+ && $tokens[$firstContent]['line'] >= ($tokens[$scopeOpener]['line'] + 2)
+ ) {
+ $gap = ($tokens[$firstContent]['line'] - $tokens[$scopeOpener]['line'] - 1);
+ $phpcsFile->recordMetric($stackPtr, 'Blank lines at start of control structure', $gap);
+
+ $error = 'Blank line found at start of control structure';
+ $fix = $phpcsFile->addFixableError($error, $scopeOpener, 'SpacingAfterOpen');
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $i = ($scopeOpener + 1);
+ while ($tokens[$i]['line'] !== $tokens[$firstContent]['line']) {
+ // Start removing content from the line after the opener.
+ if ($tokens[$i]['line'] !== $tokens[$scopeOpener]['line']) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $i++;
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Blank lines at start of control structure', 0);
+ }//end if
+
+ if ($firstContent !== $scopeCloser) {
+ $lastContent = $phpcsFile->findPrevious(
+ T_WHITESPACE,
+ ($scopeCloser - 1),
+ null,
+ true
+ );
+
+ $lastNonEmptyContent = $phpcsFile->findPrevious(
+ Tokens::$emptyTokens,
+ ($scopeCloser - 1),
+ null,
+ true
+ );
+
+ $checkToken = $lastContent;
+ if (isset($tokens[$lastNonEmptyContent]['scope_condition']) === true) {
+ $checkToken = $tokens[$lastNonEmptyContent]['scope_condition'];
+ }
+
+ if (isset($ignore[$tokens[$checkToken]['code']]) === false
+ && $tokens[$lastContent]['line'] <= ($tokens[$scopeCloser]['line'] - 2)
+ ) {
+ $errorToken = $scopeCloser;
+ for ($i = ($scopeCloser - 1); $i > $lastContent; $i--) {
+ if ($tokens[$i]['line'] < $tokens[$scopeCloser]['line']) {
+ $errorToken = $i;
+ break;
+ }
+ }
+
+ $gap = ($tokens[$scopeCloser]['line'] - $tokens[$lastContent]['line'] - 1);
+ $phpcsFile->recordMetric($stackPtr, 'Blank lines at end of control structure', $gap);
+
+ $error = 'Blank line found at end of control structure';
+ $fix = $phpcsFile->addFixableError($error, $errorToken, 'SpacingBeforeClose');
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $i = ($scopeCloser - 1);
+ for ($i = ($scopeCloser - 1); $i > $lastContent; $i--) {
+ if ($tokens[$i]['line'] === $tokens[$scopeCloser]['line']) {
+ continue;
+ }
+
+ if ($tokens[$i]['line'] === $tokens[$lastContent]['line']) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'Blank lines at end of control structure', 0);
+ }//end if
+ }//end if
+
+ $trailingContent = $phpcsFile->findNext(
+ T_WHITESPACE,
+ ($scopeCloser + 1),
+ null,
+ true
+ );
+
+ if ($tokens[$trailingContent]['code'] === T_COMMENT) {
+ // Special exception for code where the comment about
+ // an ELSE or ELSEIF is written between the control structures.
+ $nextCode = $phpcsFile->findNext(
+ Tokens::$emptyTokens,
+ ($scopeCloser + 1),
+ null,
+ true
+ );
+
+ if ($tokens[$nextCode]['code'] === T_ELSE
+ || $tokens[$nextCode]['code'] === T_ELSEIF
+ ) {
+ $trailingContent = $nextCode;
+ }
+ }//end if
+
+ if ($tokens[$trailingContent]['code'] === T_ELSE) {
+ if ($tokens[$stackPtr]['code'] === T_IF) {
+ // IF with ELSE.
+ return;
+ }
+ }
+
+ if ($tokens[$trailingContent]['code'] === T_WHILE
+ && $tokens[$stackPtr]['code'] === T_DO
+ ) {
+ // DO with WHILE.
+ return;
+ }
+
+ if ($tokens[$trailingContent]['code'] === T_CLOSE_TAG) {
+ // At the end of the script or embedded code.
+ return;
+ }
+
+ if (isset($tokens[$trailingContent]['scope_condition']) === true
+ && $tokens[$trailingContent]['scope_condition'] !== $trailingContent
+ && isset($tokens[$trailingContent]['scope_opener']) === true
+ && $tokens[$trailingContent]['scope_opener'] !== $trailingContent
+ ) {
+ // Another control structure's closing brace.
+ $owner = $tokens[$trailingContent]['scope_condition'];
+ if ($tokens[$owner]['code'] === T_FUNCTION) {
+ // The next content is the closing brace of a function
+ // so normal function rules apply and we can ignore it.
+ return;
+ }
+
+ if ($tokens[$owner]['code'] === T_CLOSURE
+ && ($phpcsFile->hasCondition($stackPtr, T_FUNCTION) === true
+ || $phpcsFile->hasCondition($stackPtr, T_CLOSURE) === true
+ || isset($tokens[$stackPtr]['nested_parenthesis']) === true)
+ ) {
+ return;
+ }
+
+ if ($tokens[$trailingContent]['line'] !== ($tokens[$scopeCloser]['line'] + 1)) {
+ $error = 'Blank line found after control structure';
+ $fix = $phpcsFile->addFixableError($error, $scopeCloser, 'LineAfterClose');
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $i = ($scopeCloser + 1);
+ while ($tokens[$i]['line'] !== $tokens[$trailingContent]['line']) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ $i++;
+ }
+
+ $phpcsFile->fixer->addNewline($scopeCloser);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ } else if ($tokens[$trailingContent]['code'] !== T_ELSE
+ && $tokens[$trailingContent]['code'] !== T_ELSEIF
+ && $tokens[$trailingContent]['code'] !== T_CATCH
+ && $tokens[$trailingContent]['line'] === ($tokens[$scopeCloser]['line'] + 1)
+ ) {
+ $error = 'No blank line found after control structure';
+ $fix = $phpcsFile->addFixableError($error, $scopeCloser, 'NoLineAfterClose');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewline($scopeCloser);
+ }
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that there is one empty line before the closing brace of a function.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class FunctionClosingBraceSpaceSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_FUNCTION,
+ T_CLOSURE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ // Probably an interface method.
+ return;
+ }
+
+ $closeBrace = $tokens[$stackPtr]['scope_closer'];
+ $prevContent = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBrace - 1), null, true);
+
+ // Special case for empty JS functions.
+ if ($phpcsFile->tokenizerType === 'JS' && $prevContent === $tokens[$stackPtr]['scope_opener']) {
+ // In this case, the opening and closing brace must be
+ // right next to each other.
+ if ($tokens[$stackPtr]['scope_closer'] !== ($tokens[$stackPtr]['scope_opener'] + 1)) {
+ $error = 'The opening and closing braces of empty functions must be directly next to each other; e.g., function () {}';
+ $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpacingBetween');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($tokens[$stackPtr]['scope_opener'] + 1); $i < $closeBrace; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ return;
+ }
+
+ $nestedFunction = false;
+ if ($phpcsFile->hasCondition($stackPtr, T_FUNCTION) === true
+ || $phpcsFile->hasCondition($stackPtr, T_CLOSURE) === true
+ || isset($tokens[$stackPtr]['nested_parenthesis']) === true
+ ) {
+ $nestedFunction = true;
+ }
+
+ $braceLine = $tokens[$closeBrace]['line'];
+ $prevLine = $tokens[$prevContent]['line'];
+ $found = ($braceLine - $prevLine - 1);
+
+ $afterKeyword = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ $beforeKeyword = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($nestedFunction === true) {
+ if ($found < 0) {
+ $error = 'Closing brace of nested function must be on a new line';
+ $fix = $phpcsFile->addFixableError($error, $closeBrace, 'ContentBeforeClose');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewlineBefore($closeBrace);
+ }
+ } else if ($found > 0) {
+ $error = 'Expected 0 blank lines before closing brace of nested function; %s found';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpacingBeforeNestedClose', $data);
+
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $changeMade = false;
+ for ($i = ($prevContent + 1); $i < $closeBrace; $i++) {
+ // Try and maintain indentation.
+ if ($tokens[$i]['line'] === ($braceLine - 1)) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ $changeMade = true;
+ }
+
+ // Special case for when the last content contains the newline
+ // token as well, like with a comment.
+ if ($changeMade === false) {
+ $phpcsFile->fixer->replaceToken(($prevContent + 1), '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+ }//end if
+ } else {
+ if ($found !== 1) {
+ if ($found < 0) {
+ $found = 0;
+ }
+
+ $error = 'Expected 1 blank line before closing function brace; %s found';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpacingBeforeClose', $data);
+
+ if ($fix === true) {
+ if ($found > 1) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($prevContent + 1); $i < ($closeBrace - 1); $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->replaceToken($i, $phpcsFile->eolChar);
+ $phpcsFile->fixer->endChangeset();
+ } else {
+ // Try and maintain indentation.
+ if ($tokens[($closeBrace - 1)]['code'] === T_WHITESPACE) {
+ $phpcsFile->fixer->addNewlineBefore($closeBrace - 1);
+ } else {
+ $phpcsFile->fixer->addNewlineBefore($closeBrace);
+ }
+ }
+ }
+ }//end if
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that there is no empty line after the opening brace of a function.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class FunctionOpeningBraceSpaceSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_FUNCTION,
+ T_CLOSURE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 (isset($tokens[$stackPtr]['scope_opener']) === false) {
+ // Probably an interface method.
+ return;
+ }
+
+ $openBrace = $tokens[$stackPtr]['scope_opener'];
+ $nextContent = $phpcsFile->findNext(T_WHITESPACE, ($openBrace + 1), null, true);
+
+ if ($nextContent === $tokens[$stackPtr]['scope_closer']) {
+ // The next bit of content is the closing brace, so this
+ // is an empty function and should have a blank line
+ // between the opening and closing braces.
+ return;
+ }
+
+ $braceLine = $tokens[$openBrace]['line'];
+ $nextLine = $tokens[$nextContent]['line'];
+
+ $found = ($nextLine - $braceLine - 1);
+ if ($found > 0) {
+ $error = 'Expected 0 blank lines after opening function brace; %s found';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $openBrace, 'SpacingAfter', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($openBrace + 1); $i < $nextContent; $i++) {
+ if ($tokens[$i]['line'] === $nextLine) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->addNewline($openBrace);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the separation between methods in a class or interface.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class FunctionSpacingSniff implements Sniff
+{
+
+ /**
+ * The number of blank lines between functions.
+ *
+ * @var integer
+ */
+ public $spacing = 2;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_FUNCTION);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+ $this->spacing = (int) $this->spacing;
+
+ /*
+ Check the number of blank lines
+ after the function.
+ */
+
+ if (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ // Must be an interface method, so the closer is the semicolon.
+ $closer = $phpcsFile->findNext(T_SEMICOLON, $stackPtr);
+ } else {
+ $closer = $tokens[$stackPtr]['scope_closer'];
+ }
+
+ // Allow for comments on the same line as the closer.
+ for ($nextLineToken = ($closer + 1); $nextLineToken < $phpcsFile->numTokens; $nextLineToken++) {
+ if ($tokens[$nextLineToken]['line'] !== $tokens[$closer]['line']) {
+ break;
+ }
+ }
+
+ $foundLines = 0;
+ if ($nextLineToken === ($phpcsFile->numTokens - 1)) {
+ // We are at the end of the file.
+ // Don't check spacing after the function because this
+ // should be done by an EOF sniff.
+ $foundLines = $this->spacing;
+ } else {
+ $nextContent = $phpcsFile->findNext(T_WHITESPACE, $nextLineToken, null, true);
+ if ($nextContent === false) {
+ // We are at the end of the file.
+ // Don't check spacing after the function because this
+ // should be done by an EOF sniff.
+ $foundLines = $this->spacing;
+ } else {
+ $foundLines += ($tokens[$nextContent]['line'] - $tokens[$nextLineToken]['line']);
+ }
+ }
+
+ if ($foundLines !== $this->spacing) {
+ $error = 'Expected %s blank line';
+ if ($this->spacing !== 1) {
+ $error .= 's';
+ }
+
+ $error .= ' after function; %s found';
+ $data = array(
+ $this->spacing,
+ $foundLines,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $closer, 'After', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = $nextLineToken; $i <= $nextContent; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$nextContent]['line']) {
+ $phpcsFile->fixer->addContentBefore($i, str_repeat($phpcsFile->eolChar, $this->spacing));
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+ }//end if
+
+ /*
+ Check the number of blank lines
+ before the function.
+ */
+
+ $prevLineToken = null;
+ for ($i = $stackPtr; $i > 0; $i--) {
+ if (strpos($tokens[$i]['content'], $phpcsFile->eolChar) === false) {
+ continue;
+ } else {
+ $prevLineToken = $i;
+ break;
+ }
+ }
+
+ if (is_null($prevLineToken) === true) {
+ // Never found the previous line, which means
+ // there are 0 blank lines before the function.
+ $foundLines = 0;
+ $prevContent = 0;
+ } else {
+ $currentLine = $tokens[$stackPtr]['line'];
+
+ $prevContent = $phpcsFile->findPrevious(T_WHITESPACE, $prevLineToken, null, true);
+ if ($tokens[$prevContent]['code'] === T_COMMENT) {
+ // Ignore comments as they can have different spacing rules, and this
+ // isn't a proper function comment anyway.
+ return;
+ }
+
+ if ($tokens[$prevContent]['code'] === T_DOC_COMMENT_CLOSE_TAG
+ && $tokens[$prevContent]['line'] === ($currentLine - 1)
+ ) {
+ // Account for function comments.
+ $prevContent = $phpcsFile->findPrevious(T_WHITESPACE, ($tokens[$prevContent]['comment_opener'] - 1), null, true);
+ }
+
+ // Before we throw an error, check that we are not throwing an error
+ // for another function. We don't want to error for no blank lines after
+ // the previous function and no blank lines before this one as well.
+ $prevLine = ($tokens[$prevContent]['line'] - 1);
+ $i = ($stackPtr - 1);
+ $foundLines = 0;
+ while ($currentLine !== $prevLine && $currentLine > 1 && $i > 0) {
+ if (isset($tokens[$i]['scope_condition']) === true) {
+ $scopeCondition = $tokens[$i]['scope_condition'];
+ if ($tokens[$scopeCondition]['code'] === T_FUNCTION) {
+ // Found a previous function.
+ return;
+ }
+ } else if ($tokens[$i]['code'] === T_FUNCTION) {
+ // Found another interface function.
+ return;
+ }
+
+ $currentLine = $tokens[$i]['line'];
+ if ($currentLine === $prevLine) {
+ break;
+ }
+
+ if ($tokens[($i - 1)]['line'] < $currentLine && $tokens[($i + 1)]['line'] > $currentLine) {
+ // This token is on a line by itself. If it is whitespace, the line is empty.
+ if ($tokens[$i]['code'] === T_WHITESPACE) {
+ $foundLines++;
+ }
+ }
+
+ $i--;
+ }//end while
+ }//end if
+
+ if ($foundLines !== $this->spacing) {
+ $error = 'Expected %s blank line';
+ if ($this->spacing !== 1) {
+ $error .= 's';
+ }
+
+ $error .= ' before function; %s found';
+ $data = array(
+ $this->spacing,
+ $foundLines,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Before', $data);
+ if ($fix === true) {
+ if ($prevContent === 0) {
+ $nextSpace = 0;
+ } else {
+ $nextSpace = $phpcsFile->findNext(T_WHITESPACE, ($prevContent + 1), $stackPtr);
+ if ($nextSpace === false) {
+ $nextSpace = ($stackPtr - 1);
+ }
+ }
+
+ if ($foundLines < $this->spacing) {
+ $padding = str_repeat($phpcsFile->eolChar, ($this->spacing - $foundLines));
+ $phpcsFile->fixer->addContent($nextSpace, $padding);
+ } else {
+ $nextContent = $phpcsFile->findNext(T_WHITESPACE, ($nextSpace + 1), null, true);
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = $nextSpace; $i < ($nextContent - 1); $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->replaceToken($i, str_repeat($phpcsFile->eolChar, $this->spacing));
+ $phpcsFile->fixer->endChangeset();
+ }
+ }//end if
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures all language constructs contain a single space between themselves and their 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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class LanguageConstructSpacingSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_ECHO,
+ T_PRINT,
+ T_RETURN,
+ T_INCLUDE,
+ T_INCLUDE_ONCE,
+ T_REQUIRE,
+ T_REQUIRE_ONCE,
+ T_NEW,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 + 1)]['code'] === T_SEMICOLON) {
+ // No content for this language construct.
+ return;
+ }
+
+ if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
+ $content = $tokens[($stackPtr + 1)]['content'];
+ $contentLength = strlen($content);
+ if ($contentLength !== 1) {
+ $error = 'Language constructs must be followed by a single space; expected 1 space but found %s';
+ $data = array($contentLength);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'IncorrectSingle', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+ } else if ($tokens[($stackPtr + 1)]['code'] !== T_OPEN_PARENTHESIS) {
+ $error = 'Language constructs must be followed by a single space; expected "%s" but found "%s"';
+ $data = array(
+ $tokens[$stackPtr]['content'].' '.$tokens[($stackPtr + 1)]['content'],
+ $tokens[$stackPtr]['content'].$tokens[($stackPtr + 1)]['content'],
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Incorrect', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($stackPtr, ' ');
+ }
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that operators have valid spacing surrounding them.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class LogicalOperatorSpacingSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return Tokens::$booleanOperators;
+
+ }//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();
+
+ // Check there is one space before the operator.
+ if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE) {
+ $error = 'Expected 1 space before logical operator; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBefore');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContentBefore($stackPtr, ' ');
+ }
+ } else {
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($tokens[$stackPtr]['line'] === $tokens[$prev]['line']
+ && strlen($tokens[($stackPtr - 1)]['content']) !== 1
+ ) {
+ $found = strlen($tokens[($stackPtr - 1)]['content']);
+ $error = 'Expected 1 space before logical operator; %s found';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'TooMuchSpaceBefore', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
+ }
+ }
+ }
+
+ // Check there is one space after the operator.
+ if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
+ $error = 'Expected 1 space after logical operator; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfter');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($stackPtr, ' ');
+ }
+ } else {
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($tokens[$stackPtr]['line'] === $tokens[$next]['line']
+ && strlen($tokens[($stackPtr + 1)]['content']) !== 1
+ ) {
+ $found = strlen($tokens[($stackPtr + 1)]['content']);
+ $error = 'Expected 1 space after logical operator; %s found';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'TooMuchSpaceAfter', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that class members are spaced correctly.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class MemberVarSpacingSniff extends AbstractVariableSniff
+{
+
+ /**
+ * The number of blank lines between member vars.
+ *
+ * @var integer
+ */
+ public $spacing = 1;
+
+ /**
+ * The number of blank lines before the fist member var.
+ *
+ * @var integer
+ */
+ public $spacingBeforeFirst = 1;
+
+
+ /**
+ * Processes the function tokens within the class.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position where the token was found.
+ *
+ * @return void
+ */
+ protected function processMemberVar(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $ignore = Tokens::$methodPrefixes;
+ $ignore[] = T_VAR;
+ $ignore[] = T_WHITESPACE;
+
+ $start = $stackPtr;
+ $prev = $phpcsFile->findPrevious($ignore, ($stackPtr - 1), null, true);
+ if (isset(Tokens::$commentTokens[$tokens[$prev]['code']]) === true) {
+ // Assume the comment belongs to the member var if it is on a line by itself.
+ $prevContent = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev - 1), null, true);
+ if ($tokens[$prevContent]['line'] !== $tokens[$prev]['line']) {
+ // Check the spacing, but then skip it.
+ $foundLines = ($tokens[$stackPtr]['line'] - $tokens[$prev]['line'] - 1);
+ if ($foundLines > 0) {
+ $error = 'Expected 0 blank lines after member var comment; %s found';
+ $data = array($foundLines);
+ $fix = $phpcsFile->addFixableError($error, $prev, 'AfterComment', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ // Inline comments have the newline included in the content but
+ // docblock do not.
+ if ($tokens[$prev]['code'] === T_COMMENT) {
+ $phpcsFile->fixer->replaceToken($prev, rtrim($tokens[$prev]['content']));
+ }
+
+ for ($i = ($prev + 1); $i <= $stackPtr; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$stackPtr]['line']) {
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->addNewline($prev);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }//end if
+
+ $start = $prev;
+ }//end if
+ }//end if
+
+ // There needs to be n blank lines before the var, not counting comments.
+ if ($start === $stackPtr) {
+ // No comment found.
+ $first = $phpcsFile->findFirstOnLine(Tokens::$emptyTokens, $start, true);
+ if ($first === false) {
+ $first = $start;
+ }
+ } else if ($tokens[$start]['code'] === T_DOC_COMMENT_CLOSE_TAG) {
+ $first = $tokens[$start]['comment_opener'];
+ } else {
+ $first = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($start - 1), null, true);
+ $first = $phpcsFile->findNext(Tokens::$commentTokens, ($first + 1));
+ }
+
+ // Determine if this is the first member var.
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($first - 1), null, true);
+ if ($tokens[$prev]['code'] === T_OPEN_CURLY_BRACKET
+ && isset(Tokens::$ooScopeTokens[$tokens[$tokens[$prev]['scope_condition']]['code']]) === true
+ ) {
+ $errorMsg = 'Expected %s blank line(s) before first member var; %s found';
+ $errorCode = 'FirstIncorrect';
+ $spacing = (int) $this->spacingBeforeFirst;
+ } else {
+ $errorMsg = 'Expected %s blank line(s) before member var; %s found';
+ $errorCode = 'Incorrect';
+ $spacing = (int) $this->spacing;
+ }
+
+ $foundLines = ($tokens[$first]['line'] - $tokens[$prev]['line'] - 1);
+ if ($foundLines === $spacing) {
+ return;
+ }
+
+ $data = array(
+ $spacing,
+ $foundLines,
+ );
+
+ $fix = $phpcsFile->addFixableError($errorMsg, $stackPtr, $errorCode, $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($prev + 1); $i < $first; $i++) {
+ if ($tokens[$i]['line'] === $tokens[$prev]['line']) {
+ continue;
+ }
+
+ if ($tokens[$i]['line'] === $tokens[$first]['line']) {
+ for ($x = 1; $x <= $spacing; $x++) {
+ $phpcsFile->fixer->addNewlineBefore($i);
+ }
+
+ break;
+ }
+
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+
+ }//end processMemberVar()
+
+
+ /**
+ * Processes normal variables.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position where the token was found.
+ *
+ * @return void
+ */
+ protected function processVariable(File $phpcsFile, $stackPtr)
+ {
+ /*
+ We don't care about normal variables.
+ */
+
+ }//end processVariable()
+
+
+ /**
+ * Processes variables in double quoted strings.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position where the token was found.
+ *
+ * @return void
+ */
+ protected function processVariableInString(File $phpcsFile, $stackPtr)
+ {
+ /*
+ We don't care about normal variables.
+ */
+
+ }//end processVariableInString()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure there is no whitespace before/after an object operator.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ObjectOperatorSpacingSniff implements Sniff
+{
+
+ /**
+ * Allow newlines instead of spaces.
+ *
+ * @var boolean
+ */
+ public $ignoreNewlines = false;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_OBJECT_OPERATOR,
+ T_DOUBLE_COLON,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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 - 1)]['code'] !== T_WHITESPACE) {
+ $before = 0;
+ } else {
+ if ($tokens[($stackPtr - 2)]['line'] !== $tokens[$stackPtr]['line']) {
+ $before = 'newline';
+ } else {
+ $before = $tokens[($stackPtr - 1)]['length'];
+ }
+ }
+
+ if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
+ $after = 0;
+ } else {
+ if ($tokens[($stackPtr + 2)]['line'] !== $tokens[$stackPtr]['line']) {
+ $after = 'newline';
+ } else {
+ $after = $tokens[($stackPtr + 1)]['length'];
+ }
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Spacing before object operator', $before);
+ $phpcsFile->recordMetric($stackPtr, 'Spacing after object operator', $after);
+
+ $this->checkSpacingBeforeOperator($phpcsFile, $stackPtr, $before);
+ $this->checkSpacingAfterOperator($phpcsFile, $stackPtr, $after);
+
+ }//end process()
+
+
+ /**
+ * Check the spacing before the operator.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param mixed $before The number of spaces found before the
+ * operator or the string 'newline'.
+ *
+ * @return boolean true if there was no error, false otherwise.
+ */
+ protected function checkSpacingBeforeOperator(File $phpcsFile, $stackPtr, $before)
+ {
+ if ($before !== 0
+ && ($before !== 'newline' || $this->ignoreNewlines === false)
+ ) {
+ $error = 'Space found before object operator';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Before');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), '');
+ }
+
+ return false;
+ }
+
+ return true;
+
+ }//end checkSpacingBeforeOperator()
+
+
+ /**
+ * Check the spacing after the operator.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param mixed $after The number of spaces found after the
+ * operator or the string 'newline'.
+ *
+ * @return boolean true if there was no error, false otherwise.
+ */
+ protected function checkSpacingAfterOperator(File $phpcsFile, $stackPtr, $after)
+ {
+ if ($after !== 0
+ && ($after !== 'newline' || $this->ignoreNewlines === false)
+ ) {
+ $error = 'Space found after object operator';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'After');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), '');
+ }
+
+ return false;
+ }
+
+ return true;
+
+ }//end checkSpacingAfterOperator()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Verifies that operators have valid spacing surrounding them.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class OperatorSpacingSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+ /**
+ * Allow newlines instead of spaces.
+ *
+ * @var boolean
+ */
+ public $ignoreNewlines = false;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ $comparison = Tokens::$comparisonTokens;
+ $operators = Tokens::$operators;
+ $assignment = Tokens::$assignmentTokens;
+ $inlineIf = array(
+ T_INLINE_THEN,
+ T_INLINE_ELSE,
+ );
+
+ return array_unique(
+ array_merge($comparison, $operators, $assignment, $inlineIf)
+ );
+
+ }//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();
+
+ // Skip default values in function declarations.
+ // Skip declare statements.
+ if ($tokens[$stackPtr]['code'] === T_EQUAL
+ || $tokens[$stackPtr]['code'] === T_MINUS
+ ) {
+ if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
+ $parenthesis = array_keys($tokens[$stackPtr]['nested_parenthesis']);
+ $bracket = array_pop($parenthesis);
+ if (isset($tokens[$bracket]['parenthesis_owner']) === true) {
+ $function = $tokens[$bracket]['parenthesis_owner'];
+ if ($tokens[$function]['code'] === T_FUNCTION
+ || $tokens[$function]['code'] === T_CLOSURE
+ || $tokens[$function]['code'] === T_DECLARE
+ ) {
+ return;
+ }
+ }
+ }
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_EQUAL) {
+ // Skip for '=&' case.
+ if (isset($tokens[($stackPtr + 1)]) === true
+ && $tokens[($stackPtr + 1)]['code'] === T_BITWISE_AND
+ ) {
+ return;
+ }
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_BITWISE_AND) {
+ // If it's not a reference, then we expect one space either side of the
+ // bitwise operator.
+ if ($phpcsFile->isReference($stackPtr) === true) {
+ return;
+ }
+
+ // Check there is one space before the & operator.
+ if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE) {
+ $error = 'Expected 1 space before "&" operator; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBeforeAmp');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContentBefore($stackPtr, ' ');
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Space before operator', 0);
+ } else {
+ if ($tokens[($stackPtr - 2)]['line'] !== $tokens[$stackPtr]['line']) {
+ $found = 'newline';
+ } else {
+ $found = $tokens[($stackPtr - 1)]['length'];
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Space before operator', $found);
+ if ($found !== 1
+ && ($found !== 'newline' || $this->ignoreNewlines === false)
+ ) {
+ $error = 'Expected 1 space before "&" operator; %s found';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeAmp', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
+ }
+ }
+ }//end if
+
+ // Check there is one space after the & operator.
+ if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
+ $error = 'Expected 1 space after "&" operator; 0 found';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfterAmp');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($stackPtr, ' ');
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Space after operator', 0);
+ } else {
+ if ($tokens[($stackPtr + 2)]['line'] !== $tokens[$stackPtr]['line']) {
+ $found = 'newline';
+ } else {
+ $found = $tokens[($stackPtr + 1)]['length'];
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Space after operator', $found);
+ if ($found !== 1
+ && ($found !== 'newline' || $this->ignoreNewlines === false)
+ ) {
+ $error = 'Expected 1 space after "&" operator; %s found';
+ $data = array($found);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterAmp', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+ }//end if
+
+ return;
+ }//end if
+
+ if ($tokens[$stackPtr]['code'] === T_MINUS || $tokens[$stackPtr]['code'] === T_PLUS) {
+ // Check minus spacing, but make sure we aren't just assigning
+ // a minus value or returning one.
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] === T_RETURN) {
+ // Just returning a negative value; eg. (return -1).
+ return;
+ }
+
+ if (isset(Tokens::$operators[$tokens[$prev]['code']]) === true) {
+ // Just trying to operate on a negative value; eg. ($var * -1).
+ return;
+ }
+
+ if (isset(Tokens::$comparisonTokens[$tokens[$prev]['code']]) === true) {
+ // Just trying to compare a negative value; eg. ($var === -1).
+ return;
+ }
+
+ if (isset(Tokens::$booleanOperators[$tokens[$prev]['code']]) === true) {
+ // Just trying to compare a negative value; eg. ($var || -1 === $b).
+ return;
+ }
+
+ if (isset(Tokens::$assignmentTokens[$tokens[$prev]['code']]) === true) {
+ // Just trying to assign a negative value; eg. ($var = -1).
+ return;
+ }
+
+ // A list of tokens that indicate that the token is not
+ // part of an arithmetic operation.
+ $invalidTokens = array(
+ T_COMMA => true,
+ T_OPEN_PARENTHESIS => true,
+ T_OPEN_SQUARE_BRACKET => true,
+ T_OPEN_SHORT_ARRAY => true,
+ T_DOUBLE_ARROW => true,
+ T_COLON => true,
+ T_INLINE_THEN => true,
+ T_INLINE_ELSE => true,
+ T_CASE => true,
+ );
+
+ if (isset($invalidTokens[$tokens[$prev]['code']]) === true) {
+ // Just trying to use a negative value; eg. myFunction($var, -2).
+ return;
+ }
+ }//end if
+
+ $operator = $tokens[$stackPtr]['content'];
+
+ if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE
+ && (($tokens[($stackPtr - 1)]['code'] === T_INLINE_THEN
+ && $tokens[($stackPtr )]['code'] === T_INLINE_ELSE) === false)
+ ) {
+ $error = "Expected 1 space before \"$operator\"; 0 found";
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBefore');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContentBefore($stackPtr, ' ');
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Space before operator', 0);
+ } else if (isset(Tokens::$assignmentTokens[$tokens[$stackPtr]['code']]) === false) {
+ // Don't throw an error for assignments, because other standards allow
+ // multiple spaces there to align multiple assignments.
+ if ($tokens[($stackPtr - 2)]['line'] !== $tokens[$stackPtr]['line']) {
+ $found = 'newline';
+ } else {
+ $found = $tokens[($stackPtr - 1)]['length'];
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Space before operator', $found);
+ if ($found !== 1
+ && ($found !== 'newline' || $this->ignoreNewlines === false)
+ ) {
+ $error = 'Expected 1 space before "%s"; %s found';
+ $data = array(
+ $operator,
+ $found,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBefore', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ if ($found === 'newline') {
+ $i = ($stackPtr - 2);
+ while ($tokens[$i]['code'] === T_WHITESPACE) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ $i--;
+ }
+ }
+
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
+ $phpcsFile->fixer->endChangeset();
+ }
+ }//end if
+ }//end if
+
+ if (isset($tokens[($stackPtr + 1)]) === false) {
+ return;
+ }
+
+ if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
+ // Skip short ternary such as: "$foo = $bar ?: true;".
+ if (($tokens[$stackPtr]['code'] === T_INLINE_THEN
+ && $tokens[($stackPtr + 1)]['code'] === T_INLINE_ELSE)
+ ) {
+ return;
+ }
+
+ $error = "Expected 1 space after \"$operator\"; 0 found";
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfter');
+ if ($fix === true) {
+ $phpcsFile->fixer->addContent($stackPtr, ' ');
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Space after operator', 0);
+ } else {
+ if (isset($tokens[($stackPtr + 2)]) === true
+ && $tokens[($stackPtr + 2)]['line'] !== $tokens[$stackPtr]['line']
+ ) {
+ $found = 'newline';
+ } else {
+ $found = $tokens[($stackPtr + 1)]['length'];
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Space after operator', $found);
+ if ($found !== 1
+ && ($found !== 'newline' || $this->ignoreNewlines === false)
+ ) {
+ $error = 'Expected 1 space after "%s"; %s found';
+ $data = array(
+ $operator,
+ $found,
+ );
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfter', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensures that a property or label colon has a single space after it and no space before 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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class PropertyLabelSpacingSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array('JS');
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_PROPERTY,
+ T_LABEL,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $colon = $phpcsFile->findNext(T_COLON, ($stackPtr + 1));
+
+ if ($colon !== ($stackPtr + 1)) {
+ $error = 'There must be no space before the colon in a property/label declaration';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Before');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), '');
+ }
+ }
+
+ if ($tokens[($colon + 1)]['code'] !== T_WHITESPACE || $tokens[($colon + 1)]['content'] !== ' ') {
+ $error = 'There must be a single space after the colon in a property/label declaration';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'After');
+ if ($fix === true) {
+ if ($tokens[($colon + 1)]['code'] === T_WHITESPACE) {
+ $phpcsFile->fixer->replaceToken(($colon + 1), ' ');
+ } else {
+ $phpcsFile->fixer->addContent($colon, ' ');
+ }
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that the closing braces of scopes are aligned correctly.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ScopeClosingBraceSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return Tokens::$scopeOpeners;
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document.
+ * @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 this is an inline condition (ie. there is no scope opener), then
+ // return, as this is not a new scope.
+ if (isset($tokens[$stackPtr]['scope_closer']) === false) {
+ return;
+ }
+
+ // We need to actually find the first piece of content on this line,
+ // as if this is a method with tokens before it (public, static etc)
+ // or an if with an else before it, then we need to start the scope
+ // checking from there, rather than the current token.
+ $lineStart = $phpcsFile->findFirstOnLine(array(T_WHITESPACE, T_INLINE_HTML), $stackPtr, true);
+ while ($tokens[$lineStart]['code'] === T_CONSTANT_ENCAPSED_STRING
+ && $tokens[($lineStart - 1)]['code'] === T_CONSTANT_ENCAPSED_STRING
+ ) {
+ $lineStart = $phpcsFile->findFirstOnLine(array(T_WHITESPACE, T_INLINE_HTML), ($lineStart - 1), true);
+ }
+
+ $startColumn = $tokens[$lineStart]['column'];
+ $scopeStart = $tokens[$stackPtr]['scope_opener'];
+ $scopeEnd = $tokens[$stackPtr]['scope_closer'];
+
+ // Check that the closing brace is on it's own line.
+ $lastContent = $phpcsFile->findPrevious(array(T_INLINE_HTML, T_WHITESPACE, T_OPEN_TAG), ($scopeEnd - 1), $scopeStart, true);
+ if ($tokens[$lastContent]['line'] === $tokens[$scopeEnd]['line']) {
+ $error = 'Closing brace must be on a line by itself';
+ $fix = $phpcsFile->addFixableError($error, $scopeEnd, 'ContentBefore');
+ if ($fix === true) {
+ $phpcsFile->fixer->addNewlineBefore($scopeEnd);
+ }
+
+ return;
+ }
+
+ // Check now that the closing brace is lined up correctly.
+ $lineStart = $phpcsFile->findFirstOnLine(array(T_WHITESPACE, T_INLINE_HTML), $scopeEnd, true);
+ $braceIndent = $tokens[$lineStart]['column'];
+ if ($tokens[$stackPtr]['code'] !== T_DEFAULT
+ && $tokens[$stackPtr]['code'] !== T_CASE
+ && $braceIndent !== $startColumn
+ ) {
+ $error = 'Closing brace indented incorrectly; expected %s spaces, found %s';
+ $data = array(
+ ($startColumn - 1),
+ ($braceIndent - 1),
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $scopeEnd, 'Indent', $data);
+ if ($fix === true) {
+ $diff = ($startColumn - $braceIndent);
+ if ($diff > 0) {
+ $phpcsFile->fixer->addContentBefore($lineStart, str_repeat(' ', $diff));
+ } else {
+ $phpcsFile->fixer->substrToken(($lineStart - 1), 0, $diff);
+ }
+ }
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure there is a single space after scope keywords.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ScopeKeywordSpacingSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ $register = Tokens::$scopeModifiers;
+ $register[] = T_STATIC;
+ return $register;
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+
+ if ($tokens[$stackPtr]['code'] === T_STATIC
+ && ($tokens[$nextToken]['code'] === T_DOUBLE_COLON
+ || $tokens[$prevToken]['code'] === T_NEW)
+ ) {
+ // Late static binding, e.g., static:: OR new static() usage.
+ return;
+ }
+
+ if ($tokens[$prevToken]['code'] === T_AS) {
+ // Trait visibilty change, e.g., "use HelloWorld { sayHello as private; }".
+ return;
+ }
+
+ $nextToken = $tokens[($stackPtr + 1)];
+ if (strlen($nextToken['content']) !== 1
+ || $nextToken['content'] === $phpcsFile->eolChar
+ ) {
+ $error = 'Scope keyword "%s" must be followed by a single space';
+ $data = array($tokens[$stackPtr]['content']);
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Incorrect', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
+ }
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Ensure there is no whitespace before a semicolon.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class SemicolonSpacingSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ );
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_SEMICOLON);
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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();
+
+ $prevType = $tokens[($stackPtr - 1)]['code'];
+ if (isset(Tokens::$emptyTokens[$prevType]) === false) {
+ return;
+ }
+
+ $nonSpace = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 2), null, true);
+ if ($tokens[$nonSpace]['code'] === T_SEMICOLON) {
+ // Empty statement.
+ return;
+ }
+
+ $expected = $tokens[$nonSpace]['content'].';';
+ $found = $phpcsFile->getTokensAsString($nonSpace, ($stackPtr - $nonSpace)).';';
+ $error = 'Space found before semicolon; expected "%s" but found "%s"';
+ $data = array(
+ $expected,
+ $found,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Incorrect', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $i = ($stackPtr - 1);
+ while (($tokens[$i]['code'] === T_WHITESPACE) && ($i > $nonSpace)) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ $i--;
+ }
+
+ $phpcsFile->fixer->addContent($nonSpace, ';');
+ $phpcsFile->fixer->replaceToken($stackPtr, '');
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks for unneeded whitespace.
+ *
+ * Checks that no whitespace preceeds the first content of the file, exists
+ * after the last content of the file, resides after content on any line, or
+ * are two empty lines in functions.
+ *
+ * @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\Standards\Squiz\Sniffs\WhiteSpace;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class SuperfluousWhitespaceSniff implements Sniff
+{
+
+ /**
+ * A list of tokenizers this sniff supports.
+ *
+ * @var array
+ */
+ public $supportedTokenizers = array(
+ 'PHP',
+ 'JS',
+ 'CSS',
+ );
+
+ /**
+ * If TRUE, whitespace rules are not checked for blank lines.
+ *
+ * Blank lines are those that contain only whitespace.
+ *
+ * @var boolean
+ */
+ public $ignoreBlankLines = false;
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(
+ T_OPEN_TAG,
+ T_CLOSE_TAG,
+ T_WHITESPACE,
+ T_COMMENT,
+ T_DOC_COMMENT_WHITESPACE,
+ T_CLOSURE,
+ );
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @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_OPEN_TAG) {
+ /*
+ Check for start of file whitespace.
+ */
+
+ if ($phpcsFile->tokenizerType !== 'PHP') {
+ // The first token is always the open tag inserted when tokenizsed
+ // and the second token is always the first piece of content in
+ // the file. If the second token is whitespace, there was
+ // whitespace at the start of the file.
+ if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
+ return;
+ }
+
+ if ($phpcsFile->fixer->enabled === true) {
+ $stackPtr = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ }
+ } else {
+ // If it's the first token, then there is no space.
+ if ($stackPtr === 0) {
+ return;
+ }
+
+ for ($i = ($stackPtr - 1); $i >= 0; $i--) {
+ // If we find something that isn't inline html then there is something previous in the file.
+ if ($tokens[$i]['type'] !== 'T_INLINE_HTML') {
+ return;
+ }
+
+ // If we have ended up with inline html make sure it isn't just whitespace.
+ $tokenContent = trim($tokens[$i]['content']);
+ if ($tokenContent !== '') {
+ return;
+ }
+ }
+ }//end if
+
+ $fix = $phpcsFile->addFixableError('Additional whitespace found at start of file', $stackPtr, 'StartFile');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = 0; $i < $stackPtr; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ } else if ($tokens[$stackPtr]['code'] === T_CLOSE_TAG) {
+ /*
+ Check for end of file whitespace.
+ */
+
+ if ($phpcsFile->tokenizerType === 'PHP') {
+ if (isset($tokens[($stackPtr + 1)]) === false) {
+ // The close PHP token is the last in the file.
+ return;
+ }
+
+ for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) {
+ // If we find something that isn't inline HTML then there
+ // is more to the file.
+ if ($tokens[$i]['type'] !== 'T_INLINE_HTML') {
+ return;
+ }
+
+ // If we have ended up with inline html make sure it
+ // isn't just whitespace.
+ $tokenContent = trim($tokens[$i]['content']);
+ if (empty($tokenContent) === false) {
+ return;
+ }
+ }
+ } else {
+ // The last token is always the close tag inserted when tokenized
+ // and the second last token is always the last piece of content in
+ // the file. If the second last token is whitespace, there was
+ // whitespace at the end of the file.
+ $stackPtr--;
+
+ // The pointer is now looking at the last content in the file and
+ // not the fake PHP end tag the tokenizer inserted.
+ if ($tokens[$stackPtr]['code'] !== T_WHITESPACE) {
+ return;
+ }
+
+ // Allow a single newline at the end of the last line in the file.
+ if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE
+ && $tokens[$stackPtr]['content'] === $phpcsFile->eolChar
+ ) {
+ return;
+ }
+
+ if ($phpcsFile->fixer->enabled === true) {
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ $stackPtr = ($prev + 1);
+ }
+ }//end if
+
+ $fix = $phpcsFile->addFixableError('Additional whitespace found at end of file', $stackPtr, 'EndFile');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ } else {
+ /*
+ Check for end of line whitespace.
+ */
+
+ // Ignore whitespace that is not at the end of a line.
+ if (isset($tokens[($stackPtr + 1)]['line']) === true
+ && $tokens[($stackPtr + 1)]['line'] === $tokens[$stackPtr]['line']
+ ) {
+ return;
+ }
+
+ // Ignore blank lines if required.
+ if ($this->ignoreBlankLines === true
+ && $tokens[($stackPtr - 1)]['line'] !== $tokens[$stackPtr]['line']
+ ) {
+ return;
+ }
+
+ $tokenContent = rtrim($tokens[$stackPtr]['content'], $phpcsFile->eolChar);
+ if (empty($tokenContent) === false) {
+ if ($tokenContent !== rtrim($tokenContent)) {
+ $fix = $phpcsFile->addFixableError('Whitespace found at end of line', $stackPtr, 'EndLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($stackPtr, rtrim($tokenContent).$phpcsFile->eolChar);
+ }
+ }
+ } else if ($tokens[($stackPtr - 1)]['content'] !== rtrim($tokens[($stackPtr - 1)]['content'])
+ && $tokens[($stackPtr - 1)]['line'] === $tokens[$stackPtr]['line']
+ ) {
+ $fix = $phpcsFile->addFixableError('Whitespace found at end of line', ($stackPtr - 1), 'EndLine');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($stackPtr - 1), rtrim($tokens[($stackPtr - 1)]['content']));
+ }
+ }
+
+ /*
+ Check for multiple blank lines in a function.
+ */
+
+ if (($phpcsFile->hasCondition($stackPtr, T_FUNCTION) === true
+ || $phpcsFile->hasCondition($stackPtr, T_CLOSURE) === true)
+ && $tokens[($stackPtr - 1)]['line'] < $tokens[$stackPtr]['line']
+ && $tokens[($stackPtr - 2)]['line'] === $tokens[($stackPtr - 1)]['line']
+ ) {
+ // This is an empty line and the line before this one is not
+ // empty, so this could be the start of a multiple empty
+ // line block.
+ $next = $phpcsFile->findNext(T_WHITESPACE, $stackPtr, null, true);
+ $lines = ($tokens[$next]['line'] - $tokens[$stackPtr]['line']);
+ if ($lines > 1) {
+ $error = 'Functions must not contain multiple empty lines in a row; found %s empty lines';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'EmptyLines', array($lines));
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ $i = $stackPtr;
+ while ($tokens[$i]['line'] !== $tokens[$next]['line']) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ $i++;
+ }
+
+ $phpcsFile->fixer->addNewlineBefore($i);
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+ }//end if
+ }//end if
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+$myArray['key'] = $value;
+$myArray[/* key start */'key'] = $value;
+$myArray[/* key start */'key'/* key end */] = $value;
+$myArray [ 'key' ] = $value;
+if ($array[($index + 1)] === true) {
+} else if ($array [ ($index + 1) ] === null) {
+}
+$array = [
+ 'foo' => 'bar',
+ 'bar' => 'foo',
+];
+
+if ($foo) {}
+[$a, $b] = $c;
+
+echo foo()[ 1 ];
+
+echo $this->addedCustomFunctions['nonce'];
+echo $this->deprecated_functions[ $function_name ]['version'];
+
+echo [ 1,2,3 ][0];
+echo [ 1,2,3 ][ 0 ];
+echo 'PHP'[ 0 ];
+
+$array = [];
+$var = $var[$var[$var]]]; // Syntax error
+$var = $var[$var[$var]; // Syntax error
+
+$myArray[ /* key start */'key'] = $value;
+$myArray[ /* key start */'key'/* key end */ ] = $value;
--- /dev/null
+<?php
+$myArray['key'] = $value;
+$myArray[/* key start */'key'] = $value;
+$myArray[/* key start */'key'/* key end */] = $value;
+$myArray['key'] = $value;
+if ($array[($index + 1)] === true) {
+} else if ($array[($index + 1)] === null) {
+}
+$array = [
+ 'foo' => 'bar',
+ 'bar' => 'foo',
+];
+
+if ($foo) {}
+[$a, $b] = $c;
+
+echo foo()[1];
+
+echo $this->addedCustomFunctions['nonce'];
+echo $this->deprecated_functions[$function_name]['version'];
+
+echo [ 1,2,3 ][0];
+echo [ 1,2,3 ][0];
+echo 'PHP'[0];
+
+$array = [];
+$var = $var[$var[$var]]]; // Syntax error
+$var = $var[$var[$var]; // Syntax error
+
+$myArray[/* key start */'key'] = $value;
+$myArray[/* key start */'key'/* key end */] = $value;
--- /dev/null
+<?php
+/**
+ * Unit test class for the ArrayBracketSpacing 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\Standards\Squiz\Tests\Arrays;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ArrayBracketSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 5 => 3,
+ 7 => 3,
+ 17 => 2,
+ 20 => 2,
+ 23 => 2,
+ 24 => 2,
+ 30 => 1,
+ 31 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+function test()
+{
+ $a = array();
+
+ $b = Array ();
+
+ $c = Array(
+ 'a' => 1,
+ );
+}
+
+
+class TestClass
+{
+ public $good = array(
+ 'width' => '',
+ 'height' => '',
+ );
+
+ private $_bad = Array(
+ 'width' => '',
+ 'height' => ''
+ );
+
+
+ public function test()
+ {
+ $truck = array(
+ 'width' => '',
+ 'height' => '',
+ );
+
+ $plane = Array(
+ 'width' => '',
+ 'height' => '',
+ );
+
+ $car = array(
+ 'width' => '',
+ 'height' => '',
+ );
+
+ $bus = array(
+ 'width' => '',
+ 'height' => ''
+ );
+
+ $train = array (
+ TRUE,
+ FALSE,
+ 'aaa'
+ );
+
+ $inline = array('aaa', 'bbb', 'ccc');
+ $inline = array('aaa');
+ $inline = Array('aaa');
+
+ $bigone = array(
+ 'name' => 'bigone',
+ 'children' => Array(
+ '1a' => 'child',
+ '11b' => 'child',
+ '111c' => 'child',
+ 'children' => Array(
+ 'child' => 'aaa',
+ ),
+ ),
+ 'short_name' => 'big'
+ );
+ }
+
+}//end class
+
+$value = array ( );
+$value = array( );
+$value = array('1'=>$one, '2' => $two, '3'=> $three, '4' =>$four);
+$value = array('1'=>$one);
+
+if (in_array('1', array('1','2','3')) === TRUE) {
+ $value = in_array('1', array('1' , '2', '3','4'));
+}
+
+$value = array(
+ '1'=> TRUE,
+ FALSE,
+ '3' => 'aaa',);
+
+$value = array(
+ '1'=> TRUE,
+ FALSE,
+ );
+
+$value = array(
+ TRUE,
+ '1' => FALSE,
+ );
+
+$value = array(1,
+ 2 ,
+ 3 ,
+ );
+
+$value = array(1 => $one,
+ 2 => $two ,
+ 3 => $three ,
+ );
+
+$value = array(
+ 'tag' => $tag,
+ 'space' => $this->_getIndentation($tag, $tagElement),
+ );
+
+$expected = array(
+ array(
+ '1' => 1,
+ '1' => 2,
+ ),
+ );
+
+$expected = array(
+ array(
+ '1' => 1,
+ '1' => 2
+ )
+ );
+
+// Space in second arg.
+$args = array(
+ '"'.$this->id.'"',
+ (int) $hasSessions,
+ );
+
+// No errors.
+$paths = array(
+ Init::ROOT_DIR.'/Systems' => 'Systems',
+ Init::ROOT_DIR.'/Installer' => 'Systems',
+ );
+
+$x = array(
+ );
+
+$x = array('test'
+ );
+$x = array('test',
+ );
+$x = array('name' => 'test',
+ );
+
+$x = array(
+ $x,
+ );
+
+$func = array(
+ $x,
+ 'get'.$x.'Replacement'
+ );
+
+$array = array(
+ 'input_one' => 'one',
+ 'inputTwo' => 'two',
+ 'input_3' => 3,
+ );
+
+$array = array(
+ 'input_one',
+ 'inputTwo',
+ 'input_3',
+ );
+
+// Malformed
+$foo = array(1
+, 2);
+
+$listItems[$aliasPath] = array('itemContent' => implode('<br/>', $aliases));
+
+$listItems[$aliasPath] = array(
+ 'itemContent' => implode('<br/>', $aliases)
+ );
+
+$x = array
+ (
+ $x,
+ $y,
+ );
+
+$x = array
+(
+ $x,
+ $y,
+ );
+
+$x = array(
+
+ $x,
+ $y,
+ );
+
+$test = array(
+ 'test' => TestFunction::blah(
+ $value1,
+ $value2
+ ),
+ );
+
+$c = array('a' => 1,);
+
+function b()
+{
+ $a = array(
+ 'a' => a('a'),
+
+ );
+
+}
+
+$foo = Array('[',']',':',"\n","\r");
+$bar = Array('[',']',':',' ',' ');
+
+function foo()
+{
+ return array($a, $b->screen);
+}
+
+$array = array(
+ 'name' => 'contactSubject',
+ 'required' => TRUE,
+ 'validators' => array(
+ new \Zend\Validator\InArray(array('haystack' => array_keys($aSubjects))),
+ ),
+ );
+
+$var = array(
+ 'ViewHelper',
+ array('Foo'),
+ 'Errors',
+ );
+
+$data = array(
+ 'first',
+ 'second',
+ 'third',
+ // Add more here
+ );
+
+$data = array(
+ 'first',
+ 'second',
+ //'third',
+ );
+
+$data = array(
+ 'first',
+ 'second'
+ //'third',
+ );
+
+$foo = array(
+ $this->getViewName() . '.id' => 'value',
+ $this->getViewName() . '.title' => 'value',
+ );
+
+$foo = array(
+ $this->getViewName() . '.id',
+ $this->getViewName() . '.title',
+ );
+
+$weightings = array(
+ T_CLOSURE => 100,
+
+ /*
+ Conditions.
+ */
+
+ T_WHILE => 50,
+
+ /*
+ Operators and arithmetic.
+ */
+
+ T_BITWISE_AND => 8,
+
+ T_BOOLEAN_AND => 5,
+
+ /*
+ Equality.
+ */
+
+ T_IS_GREATER_OR_EQUAL => 5,
+ );
+
+foreach (array(
+ 'foo' => 'bar',
+ 'foobaz' => 'bazzy',
+ ) as $key => $value) {
+}
+
+$ids = array(
+ '1', // Foo.
+ '13', // Bar.
+ );
+
+array(
+ 'key1' => function($bar) {
+ return $bar;
+ },
+ 'key2' => function($foo) {
+ return $foo;
+ },
+ 'key3' => function($bar) {
+ return $bar;
+ }
+);
+
+array(
+ 'key1' => array(
+ '1',
+ '2',
+ )
+);
+
+$var = array(
+ 'tab_template' => '
+ <li>%s</li>',
+ 'panel_template' => '
+ <div id="%s">
+ %s
+ </div>',
+ );
+
+function test() : array
+{
+ return [];
+}
+
+$fields = array(
+ 'id' => array('type' => 'INT'),
+ 'value' => array('type' => 'VARCHAR'));
+
+get_current_screen()->add_help_tab( array(
+ 'id' => <<<EOD
+Here comes some text.
+EOD
+,
+ ) );
+
+$a = array
+// comment
+( 'a', 'b' );
+
+$a = array /* comment */ ( 'a', 'b' );
--- /dev/null
+<?php
+
+function test()
+{
+ $a = array();
+
+ $b = array();
+
+ $c = array('a' => 1);
+}
+
+
+class TestClass
+{
+ public $good = array(
+ 'width' => '',
+ 'height' => '',
+ );
+
+ private $_bad = array(
+ 'width' => '',
+ 'height' => '',
+ );
+
+
+ public function test()
+ {
+ $truck = array(
+ 'width' => '',
+ 'height' => '',
+ );
+
+ $plane = array(
+ 'width' => '',
+ 'height' => '',
+ );
+
+ $car = array(
+ 'width' => '',
+ 'height' => '',
+ );
+
+ $bus = array(
+ 'width' => '',
+ 'height' => '',
+ );
+
+ $train = array(
+ TRUE,
+ FALSE,
+ 'aaa',
+ );
+
+ $inline = array(
+ 'aaa',
+ 'bbb',
+ 'ccc',
+ );
+ $inline = array('aaa');
+ $inline = array('aaa');
+
+ $bigone = array(
+ 'name' => 'bigone',
+ 'children' => array(
+ '1a' => 'child',
+ '11b' => 'child',
+ '111c' => 'child',
+ 'children' => array('child' => 'aaa'),
+ ),
+ 'short_name' => 'big',
+ );
+ }
+
+}//end class
+
+$value = array();
+$value = array();
+$value = array(
+ '1' => $one,
+ '2' => $two,
+ '3' => $three,
+ '4' => $four,
+ );
+$value = array('1' => $one);
+
+if (in_array('1', array('1', '2', '3')) === TRUE) {
+ $value = in_array('1', array('1', '2', '3', '4'));
+}
+
+$value = array(
+ '1'=> TRUE,
+ FALSE,
+ '3' => 'aaa',
+ );
+
+$value = array(
+ '1'=> TRUE,
+ FALSE,
+ );
+
+$value = array(
+ TRUE,
+ '1' => FALSE,
+ );
+
+$value = array(
+ 1,
+ 2,
+ 3,
+ );
+
+$value = array(
+ 1 => $one,
+ 2 => $two,
+ 3 => $three,
+ );
+
+$value = array(
+ 'tag' => $tag,
+ 'space' => $this->_getIndentation($tag, $tagElement),
+ );
+
+$expected = array(
+ array(
+ '1' => 1,
+ '1' => 2,
+ ),
+ );
+
+$expected = array(
+ array(
+ '1' => 1,
+ '1' => 2,
+ ),
+ );
+
+// Space in second arg.
+$args = array(
+ '"'.$this->id.'"',
+ (int) $hasSessions,
+ );
+
+// No errors.
+$paths = array(
+ Init::ROOT_DIR.'/Systems' => 'Systems',
+ Init::ROOT_DIR.'/Installer' => 'Systems',
+ );
+
+$x = array();
+
+$x = array('test');
+$x = array('test');
+$x = array('name' => 'test');
+
+$x = array($x);
+
+$func = array(
+ $x,
+ 'get'.$x.'Replacement',
+ );
+
+$array = array(
+ 'input_one' => 'one',
+ 'inputTwo' => 'two',
+ 'input_3' => 3,
+ );
+
+$array = array(
+ 'input_one',
+ 'inputTwo',
+ 'input_3',
+ );
+
+// Malformed
+$foo = array(
+ 1,
+ 2,
+ );
+
+$listItems[$aliasPath] = array('itemContent' => implode('<br/>', $aliases));
+
+$listItems[$aliasPath] = array(
+ 'itemContent' => implode('<br/>', $aliases),
+ );
+
+$x = array(
+ $x,
+ $y,
+ );
+
+$x = array(
+ $x,
+ $y,
+ );
+
+$x = array(
+
+ $x,
+ $y,
+ );
+
+$test = array(
+ 'test' => TestFunction::blah(
+ $value1,
+ $value2
+ ),
+ );
+
+$c = array('a' => 1);
+
+function b()
+{
+ $a = array(
+ 'a' => a('a'),
+
+ );
+
+}
+
+$foo = array(
+ '[',
+ ']',
+ ':',
+ "\n",
+ "\r",
+ );
+$bar = array(
+ '[',
+ ']',
+ ':',
+ ' ',
+ ' ',
+ );
+
+function foo()
+{
+ return array(
+ $a,
+ $b->screen,
+ );
+}
+
+$array = array(
+ 'name' => 'contactSubject',
+ 'required' => TRUE,
+ 'validators' => array(
+ new \Zend\Validator\InArray(array('haystack' => array_keys($aSubjects))),
+ ),
+ );
+
+$var = array(
+ 'ViewHelper',
+ array('Foo'),
+ 'Errors',
+ );
+
+$data = array(
+ 'first',
+ 'second',
+ 'third',
+ // Add more here
+ );
+
+$data = array(
+ 'first',
+ 'second',
+ //'third',
+ );
+
+$data = array(
+ 'first',
+ 'second',
+ //'third',
+ );
+
+$foo = array(
+ $this->getViewName() . '.id' => 'value',
+ $this->getViewName() . '.title' => 'value',
+ );
+
+$foo = array(
+ $this->getViewName() . '.id',
+ $this->getViewName() . '.title',
+ );
+
+$weightings = array(
+ T_CLOSURE => 100,
+
+ /*
+ Conditions.
+ */
+
+ T_WHILE => 50,
+
+ /*
+ Operators and arithmetic.
+ */
+
+ T_BITWISE_AND => 8,
+
+ T_BOOLEAN_AND => 5,
+
+ /*
+ Equality.
+ */
+
+ T_IS_GREATER_OR_EQUAL => 5,
+ );
+
+foreach (array(
+ 'foo' => 'bar',
+ 'foobaz' => 'bazzy',
+ ) as $key => $value) {
+}
+
+$ids = array(
+ '1', // Foo.
+ '13', // Bar.
+ );
+
+array(
+ 'key1' => function($bar) {
+ return $bar;
+ },
+ 'key2' => function($foo) {
+ return $foo;
+ },
+ 'key3' => function($bar) {
+ return $bar;
+ },
+);
+
+array(
+ 'key1' => array(
+ '1',
+ '2',
+ ),
+);
+
+$var = array(
+ 'tab_template' => '
+ <li>%s</li>',
+ 'panel_template' => '
+ <div id="%s">
+ %s
+ </div>',
+ );
+
+function test() : array
+{
+ return [];
+}
+
+$fields = array(
+ 'id' => array('type' => 'INT'),
+ 'value' => array('type' => 'VARCHAR'),
+ );
+
+get_current_screen()->add_help_tab( array(
+ 'id' => <<<EOD
+Here comes some text.
+EOD
+,
+ ) );
+
+$a = array
+// comment
+(
+ 'a',
+ 'b',
+ );
+
+$a = array /* comment */ (
+ 'a',
+ 'b',
+ );
--- /dev/null
+<?php
+
+function test()
+{
+ $a = [];
+
+ $b = [];
+
+ $c = [
+ 'a' => 1,
+ ];
+}
+
+
+class TestClass
+{
+ public $good = [
+ 'width' => '',
+ 'height' => '',
+ ];
+
+ private $_bad = [
+ 'width' => '',
+ 'height' => ''
+ ];
+
+
+ public function test()
+ {
+ $truck = [
+ 'width' => '',
+ 'height' => '',
+ ];
+
+ $plane = [
+ 'width' => '',
+ 'height' => '',
+ ];
+
+ $car = [
+ 'width' => '',
+ 'height' => '',
+ ];
+
+ $bus = [
+ 'width' => '',
+ 'height' => ''
+ ];
+
+ $train = [
+ TRUE,
+ FALSE,
+ 'aaa'
+ ];
+
+ $inline = ['aaa', 'bbb', 'ccc'];
+ $inline = ['aaa'];
+ $inline = ['aaa'];
+
+ $bigone = [
+ 'name' => 'bigone',
+ 'children' => [
+ '1a' => 'child',
+ '11b' => 'child',
+ '111c' => 'child',
+ 'children' => [
+ 'child' => 'aaa',
+ ],
+ ],
+ 'short_name' => 'big'
+ ];
+ }
+
+}//end class
+
+$value = [ ];
+$value = [ ];
+$value = ['1'=>$one, '2' => $two, '3'=> $three, '4' =>$four];
+$value = ['1'=>$one];
+
+if (in_array('1', ['1','2','3']) === TRUE) {
+ $value = in_array('1', ['1' , '2', '3','4']);
+}
+
+$value = [
+ '1'=> TRUE,
+ FALSE,
+ '3' => 'aaa',];
+
+$value = [
+ '1'=> TRUE,
+ FALSE,
+ ];
+
+$value = [
+ TRUE,
+ '1' => FALSE,
+ ];
+
+$value = [1,
+ 2 ,
+ 3 ,
+ ];
+
+$value = [1 => $one,
+ 2 => $two ,
+ 3 => $three ,
+ ];
+
+$value = [
+ 'tag' => $tag,
+ 'space' => $this->_getIndentation($tag, $tagElement),
+ ];
+
+$expected = [
+ [
+ '1' => 1,
+ '1' => 2,
+ ],
+ ];
+
+$expected = [
+ [
+ '1' => 1,
+ '1' => 2
+ ]
+ ];
+
+// Space in second arg.
+$args = [
+ '"'.$this->id.'"',
+ (int) $hasSessions,
+ ];
+
+// No errors.
+$paths = [
+ Init::ROOT_DIR.'/Systems' => 'Systems',
+ Init::ROOT_DIR.'/Installer' => 'Systems',
+ ];
+
+$x = [
+ ];
+
+$x = ['test'
+ ];
+$x = ['test',
+ ];
+$x = ['name' => 'test',
+ ];
+
+$x = [
+ $x,
+ ];
+
+$func = [
+ $x,
+ 'get'.$x.'Replacement'
+ ];
+
+$array = [
+ 'input_one' => 'one',
+ 'inputTwo' => 'two',
+ 'input_3' => 3,
+ ];
+
+$array = [
+ 'input_one',
+ 'inputTwo',
+ 'input_3',
+ ];
+
+// Malformed
+$foo = [1
+, 2];
+
+$listItems[$aliasPath] = ['itemContent' => implode('<br/>', $aliases)];
+
+$listItems[$aliasPath] = [
+ 'itemContent' => implode('<br/>', $aliases)
+ ];
+
+$x =
+ [
+ $x,
+ $y,
+ ];
+
+$x =
+[
+ $x,
+ $y,
+ ];
+
+$x = [
+
+ $x,
+ $y,
+ ];
+
+$test = [
+ 'test' => TestFunction::blah(
+ $value1,
+ $value2
+ ),
+ ];
+
+$c = ['a' => 1,];
+$c->{$var}[ ] = 2;
+
+$foo = ['[',']',':',"\n","\r"];
+$bar = ['[',']',':',' ',' '];
+
+function foo()
+{
+ return [$a, $b->screen];
+}
+
+$array = [
+ 'name' => 'contactSubject',
+ 'required' => TRUE,
+ 'validators' => [
+ new \Zend\Validator\InArray(['haystack' => array_keys($aSubjects)]),
+ ],
+ ];
+
+$var = [
+ 'ViewHelper',
+ ['Foo'],
+ 'Errors',
+ ];
+
+$data = [
+ 'first',
+ 'second',
+ 'third',
+ // Add more here
+ ];
+
+$data = [
+ 'first',
+ 'second',
+ //'third',
+ ];
+
+$data = [
+ 'first',
+ 'second'
+ //'third',
+ ];
+
+$foo = [
+ $this->getViewName() . '.id' => 'value',
+ $this->getViewName() . '.title' => 'value',
+ ];
+
+$foo = [
+ $this->getViewName() . '.id',
+ $this->getViewName() . '.title',
+ ];
+
+$weightings = [
+ T_CLOSURE => 100,
+
+ /*
+ Conditions.
+ */
+
+ T_WHILE => 50,
+
+ /*
+ Operators and arithmetic.
+ */
+
+ T_BITWISE_AND => 8,
+
+ T_BOOLEAN_AND => 5,
+
+ /*
+ Equality.
+ */
+
+ T_IS_GREATER_OR_EQUAL => 5,
+ ];
+
+foreach ([
+ 'foo' => 'bar',
+ 'foobaz' => 'bazzy',
+ ] as $key => $value) {
+}
+
+$ids = [
+ '1', // Foo.
+ '13', // Bar.
+ ];
+
+[
+ 'key1' => function($bar) {
+ return $bar;
+ },
+ 'key2' => function($foo) {
+ return $foo;
+ },
+ 'key3' => function($bar) {
+ return $bar;
+ }
+];
+
+[
+ 'key1' => [
+ '1',
+ '2',
+ ]
+];
+
+$var = [
+ 'tab_template' => '
+ <li>%s</li>',
+ 'panel_template' => '
+ <div id="%s">
+ %s
+ </div>',
+ ];
+
+function test() : array
+{
+ return [];
+}
+
+$fields = [
+ 'id' => ['type' => 'INT'],
+ 'value' => ['type' => 'VARCHAR']];
+
+get_current_screen()->add_help_tab( [
+ 'id' => <<<EOD
+Here comes some text.
+EOD
+,
+ ] );
+
+echo [1][0];
+echo 'PHP'[0];
+echo [1][0, 1, 2]; // not valid code, but should not be picked up here
--- /dev/null
+<?php
+
+function test()
+{
+ $a = [];
+
+ $b = [];
+
+ $c = ['a' => 1];
+}
+
+
+class TestClass
+{
+ public $good = [
+ 'width' => '',
+ 'height' => '',
+ ];
+
+ private $_bad = [
+ 'width' => '',
+ 'height' => '',
+ ];
+
+
+ public function test()
+ {
+ $truck = [
+ 'width' => '',
+ 'height' => '',
+ ];
+
+ $plane = [
+ 'width' => '',
+ 'height' => '',
+ ];
+
+ $car = [
+ 'width' => '',
+ 'height' => '',
+ ];
+
+ $bus = [
+ 'width' => '',
+ 'height' => '',
+ ];
+
+ $train = [
+ TRUE,
+ FALSE,
+ 'aaa',
+ ];
+
+ $inline = [
+ 'aaa',
+ 'bbb',
+ 'ccc',
+ ];
+ $inline = ['aaa'];
+ $inline = ['aaa'];
+
+ $bigone = [
+ 'name' => 'bigone',
+ 'children' => [
+ '1a' => 'child',
+ '11b' => 'child',
+ '111c' => 'child',
+ 'children' => ['child' => 'aaa'],
+ ],
+ 'short_name' => 'big',
+ ];
+ }
+
+}//end class
+
+$value = [];
+$value = [];
+$value = [
+ '1' => $one,
+ '2' => $two,
+ '3' => $three,
+ '4' => $four,
+ ];
+$value = ['1' => $one];
+
+if (in_array('1', ['1', '2', '3']) === TRUE) {
+ $value = in_array('1', ['1', '2', '3', '4']);
+}
+
+$value = [
+ '1'=> TRUE,
+ FALSE,
+ '3' => 'aaa',
+ ];
+
+$value = [
+ '1'=> TRUE,
+ FALSE,
+ ];
+
+$value = [
+ TRUE,
+ '1' => FALSE,
+ ];
+
+$value = [
+ 1,
+ 2,
+ 3,
+ ];
+
+$value = [
+ 1 => $one,
+ 2 => $two,
+ 3 => $three,
+ ];
+
+$value = [
+ 'tag' => $tag,
+ 'space' => $this->_getIndentation($tag, $tagElement),
+ ];
+
+$expected = [
+ [
+ '1' => 1,
+ '1' => 2,
+ ],
+ ];
+
+$expected = [
+ [
+ '1' => 1,
+ '1' => 2,
+ ],
+ ];
+
+// Space in second arg.
+$args = [
+ '"'.$this->id.'"',
+ (int) $hasSessions,
+ ];
+
+// No errors.
+$paths = [
+ Init::ROOT_DIR.'/Systems' => 'Systems',
+ Init::ROOT_DIR.'/Installer' => 'Systems',
+ ];
+
+$x = [];
+
+$x = ['test'];
+$x = ['test'];
+$x = ['name' => 'test'];
+
+$x = [$x];
+
+$func = [
+ $x,
+ 'get'.$x.'Replacement',
+ ];
+
+$array = [
+ 'input_one' => 'one',
+ 'inputTwo' => 'two',
+ 'input_3' => 3,
+ ];
+
+$array = [
+ 'input_one',
+ 'inputTwo',
+ 'input_3',
+ ];
+
+// Malformed
+$foo = [
+ 1,
+ 2,
+ ];
+
+$listItems[$aliasPath] = ['itemContent' => implode('<br/>', $aliases)];
+
+$listItems[$aliasPath] = [
+ 'itemContent' => implode('<br/>', $aliases),
+ ];
+
+$x =
+ [
+ $x,
+ $y,
+ ];
+
+$x =
+[
+ $x,
+ $y,
+];
+
+$x = [
+
+ $x,
+ $y,
+ ];
+
+$test = [
+ 'test' => TestFunction::blah(
+ $value1,
+ $value2
+ ),
+ ];
+
+$c = ['a' => 1];
+$c->{$var}[ ] = 2;
+
+$foo = [
+ '[',
+ ']',
+ ':',
+ "\n",
+ "\r",
+ ];
+$bar = [
+ '[',
+ ']',
+ ':',
+ ' ',
+ ' ',
+ ];
+
+function foo()
+{
+ return [
+ $a,
+ $b->screen,
+ ];
+}
+
+$array = [
+ 'name' => 'contactSubject',
+ 'required' => TRUE,
+ 'validators' => [
+ new \Zend\Validator\InArray(['haystack' => array_keys($aSubjects)]),
+ ],
+ ];
+
+$var = [
+ 'ViewHelper',
+ ['Foo'],
+ 'Errors',
+ ];
+
+$data = [
+ 'first',
+ 'second',
+ 'third',
+ // Add more here
+ ];
+
+$data = [
+ 'first',
+ 'second',
+ //'third',
+ ];
+
+$data = [
+ 'first',
+ 'second',
+ //'third',
+ ];
+
+$foo = [
+ $this->getViewName() . '.id' => 'value',
+ $this->getViewName() . '.title' => 'value',
+ ];
+
+$foo = [
+ $this->getViewName() . '.id',
+ $this->getViewName() . '.title',
+ ];
+
+$weightings = [
+ T_CLOSURE => 100,
+
+ /*
+ Conditions.
+ */
+
+ T_WHILE => 50,
+
+ /*
+ Operators and arithmetic.
+ */
+
+ T_BITWISE_AND => 8,
+
+ T_BOOLEAN_AND => 5,
+
+ /*
+ Equality.
+ */
+
+ T_IS_GREATER_OR_EQUAL => 5,
+ ];
+
+foreach ([
+ 'foo' => 'bar',
+ 'foobaz' => 'bazzy',
+ ] as $key => $value) {
+}
+
+$ids = [
+ '1', // Foo.
+ '13', // Bar.
+ ];
+
+[
+ 'key1' => function($bar) {
+ return $bar;
+ },
+ 'key2' => function($foo) {
+ return $foo;
+ },
+ 'key3' => function($bar) {
+ return $bar;
+ },
+];
+
+[
+ 'key1' => [
+ '1',
+ '2',
+ ],
+];
+
+$var = [
+ 'tab_template' => '
+ <li>%s</li>',
+ 'panel_template' => '
+ <div id="%s">
+ %s
+ </div>',
+ ];
+
+function test() : array
+{
+ return [];
+}
+
+$fields = [
+ 'id' => ['type' => 'INT'],
+ 'value' => ['type' => 'VARCHAR'],
+ ];
+
+get_current_screen()->add_help_tab( [
+ 'id' => <<<EOD
+Here comes some text.
+EOD
+,
+ ] );
+
+echo [1][0];
+echo 'PHP'[0];
+echo [1][0, 1, 2]; // not valid code, but should not be picked up here
--- /dev/null
+<?php
+/**
+ * Unit test class for the ArrayDeclaration 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\Standards\Squiz\Tests\Arrays;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ArrayDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ switch ($testFile) {
+ case 'ArrayDeclarationUnitTest.1.inc':
+ return array(
+ 7 => 2,
+ 9 => 2,
+ 22 => 1,
+ 23 => 1,
+ 24 => 1,
+ 25 => 1,
+ 31 => 1,
+ 35 => 1,
+ 36 => 1,
+ 41 => 1,
+ 46 => 1,
+ 47 => 1,
+ 50 => 1,
+ 51 => 1,
+ 53 => 1,
+ 56 => 1,
+ 58 => 1,
+ 61 => 1,
+ 62 => 1,
+ 63 => 1,
+ 64 => 1,
+ 65 => 1,
+ 66 => 3,
+ 70 => 1,
+ 76 => 2,
+ 77 => 1,
+ 78 => 7,
+ 79 => 2,
+ 81 => 2,
+ 82 => 4,
+ 87 => 1,
+ 88 => 1,
+ 92 => 1,
+ 97 => 1,
+ 100 => 1,
+ 101 => 1,
+ 102 => 1,
+ 105 => 1,
+ 106 => 1,
+ 107 => 1,
+ 125 => 1,
+ 126 => 1,
+ 141 => 1,
+ 144 => 1,
+ 146 => 1,
+ 148 => 1,
+ 151 => 1,
+ 157 => 1,
+ 174 => 3,
+ 179 => 1,
+ 182 => 1,
+ 188 => 1,
+ 207 => 1,
+ 212 => 1,
+ 214 => 1,
+ 218 => 2,
+ 219 => 2,
+ 223 => 1,
+ 255 => 1,
+ 294 => 1,
+ 295 => 1,
+ 296 => 1,
+ 311 => 1,
+ 317 => 1,
+ 339 => 2,
+ 348 => 2,
+ 352 => 2,
+ );
+ case 'ArrayDeclarationUnitTest.2.inc':
+ return array(
+ 9 => 1,
+ 23 => 1,
+ 24 => 1,
+ 25 => 1,
+ 31 => 1,
+ 36 => 1,
+ 41 => 1,
+ 46 => 1,
+ 47 => 1,
+ 51 => 1,
+ 53 => 1,
+ 56 => 1,
+ 61 => 1,
+ 63 => 1,
+ 64 => 1,
+ 65 => 1,
+ 66 => 2,
+ 70 => 1,
+ 76 => 1,
+ 77 => 1,
+ 78 => 7,
+ 79 => 2,
+ 81 => 2,
+ 82 => 4,
+ 87 => 1,
+ 88 => 1,
+ 92 => 1,
+ 97 => 1,
+ 100 => 1,
+ 101 => 1,
+ 102 => 1,
+ 105 => 1,
+ 106 => 1,
+ 107 => 1,
+ 125 => 1,
+ 126 => 1,
+ 141 => 1,
+ 144 => 1,
+ 146 => 1,
+ 148 => 1,
+ 151 => 1,
+ 157 => 1,
+ 174 => 3,
+ 179 => 1,
+ 190 => 1,
+ 191 => 1,
+ 192 => 1,
+ 207 => 1,
+ 210 => 1,
+ 211 => 1,
+ 215 => 1,
+ 247 => 1,
+ 286 => 1,
+ 287 => 1,
+ 288 => 1,
+ 303 => 1,
+ 309 => 1,
+ 331 => 2,
+ );
+ default:
+ return array();
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+.my-style {
+}
+
+
+.my-style {
+}
+
+/* Comment */
+
+.my-style {
+}
+
+
+/* Comment */
+
+.my-style {
+ float: left;
+
+}
+
+.AssetLineageWidgetType-item {
+ color: #CCC;
+}
+
+/*.AssetLineageWidgetType-item2 .selected,
+.AssetLineageWidgetType-item .selected {
+}*/
+
+.AssetLineageWidgetType-item.selected {
+}
+
+@media screen and (max-device-width: 769px) {
+
+ header #logo img {
+ max-width: 100%;
+ }
+
+}
+
+@media screen and (max-device-width: 769px) {
+
+ header #logo img {
+ max-width: 100%;
+ }
+}
+
+.GUITextBox.container:after {}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClassDefinitionClosingBraceSpace 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClassDefinitionClosingBraceSpaceUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 11 => 1,
+ 44 => 1,
+ 47 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+.AssetListingEditWidgetType-BottomPanel select,
+#EditEditingModeWidgetType-assetEditor-filters-assetTypes-addNew {
+ float: left;
+}
+
+.AssetListingEditWidgetType-BottomPanel select,
+
+#EditEditingModeWidgetType-assetEditor-filters-assetTypes-addNew {
+ float: left;
+}
+
+.AssetListingEditWidgetType-BottomPanel select,
+/*.AssetListingEditWidgetType-BottomPanel ul,*/
+#EditEditingModeWidgetType-assetEditor-filters-assetTypes-addNew {
+ float: left;
+}
+
+.AssetListingEditWidgetType-BottomPanel select,
+
+.AssetListingEditWidgetType-BottomPanel ul,
+#EditEditingModeWidgetType-assetEditor-filters-assetTypes-addNew {
+ float: left;
+}
+
+#SuperUsersSystemConfigScreen-table {
+ display: block;
+ left: 50%;
+ margin-left: -500px;
+ margin-top: 180px;
+ position: relative;
+ width: 1000px;
+}
+
+/**
+ * More styles below here.
+ */
+
+td.TableWidgetType-header.TableWidgetType-header-lastLogin,
+td.TableWidgetType-header.TableWidgetType-header-remove,
+td.TableWidgetType-header.TableWidgetType-header-email,
+td.TableWidgetType-header.TableWidgetType-header-userName {
+ background: url(images/ScreenImages/table_header_bg.png) repeat-x;
+ border-top: 1px solid #D4D4D4;
+ color: #787878;
+ height: 33px;
+ padding-left: 12px;
+ width: 150px;
+}
+
+@media screen and (max-device-width: 769px) {
+
+ header #logo img {
+ max-width: 100%;
+ padding: 20px;
+ margin: 40px;
+ }
+}
+
+.foo
+{
+ border: none;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClassDefinitionNameSpacing 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClassDefinitionNameSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 7 => 1,
+ 19 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
+.HelpWidgetType-new-bug-title{
+ float: left;
+}
+.HelpWidgetType-new-bug-title {
+
+ float: left;
+}
+
+@media screen and (max-device-width: 769px) {
+
+ header #logo img {
+ max-width: 100%;
+ }
+
+}
+
+@media screen and (max-device-width: 769px) {
+ header #logo img {
+ max-width: 100%;
+ }
+
+}
+
+@media screen and (max-device-width: 769px) {
+
+
+
+ header #logo img {
+ max-width: 100%;
+ }
+
+}
+
+.GUITextBox.container:after {}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClassDefinitionOpeningBraceSpace 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClassDefinitionOpeningBraceSpaceUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 4 => 1,
+ 7 => 1,
+ 10 => 1,
+ 26 => 1,
+ 33 => 1,
+ 43 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+body {
+font-family: Arial, Helvetica, sans-serif;
+margin : 40px 0 0 0;
+padding : 0;
+background: #8FB7DB url(diag_lines_bg.gif) top left;
+margin-top:
+10px;
+margin-bottom:
+0px;
+}
+
+.TableWidgetType .recover:hover {
+ background-color: #FFF;
+}
+
+#clearCache-settings:rootNodes-list_0 {
+ border-top: none;
+}
+
+.LookupEditScreenWidgetType-urls a, .LookupEditScreenWidgetType-urls a:visited {
+ text-decoration: none;
+ color: #444;
+}
+
+/* checking embedded PHP */
+li {
+ background:url(<?php print $staticserver; ?>/images/<?php print $staticdir; ?>/bullet.gif) left <?php print $left; ?>px no-repeat;
+ margin:0px;
+ padding-left:10px;
+ margin-bottom:<?php echo $marginBottom; ?>px;
+ margin-top: <?php echo $marginTop; ?>px;
+ line-height:13px;
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr=#2e62a8, endColorstr=#123363);
+}
+
+/* empty style def */
+.p {
+ margin:;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ColonSpacing 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ColonSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 4 => 2,
+ 5 => 1,
+ 6 => 1,
+ 8 => 1,
+ 27 => 1,
+ 28 => 1,
+ 29 => 1,
+ 30 => 1,
+ 32 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+#title-bar-bottom-right {
+ background-color: #333333;
+ padding: 10px;
+ border-bottom: 1px dotted #F0F0F0;
+ border-top: 1px dotted #FF00FF;
+ background: #08f7db url(diag_lines_bg.gif) top left;
+}
+
+#add-new-comment {
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ColourDefinition 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ColourDefinitionUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 5 => 1,
+ 6 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+.SettingsTabPaneWidgetType-tab-mid {
+ background: transparent url(tab_inact_mid.png) repeat-x;
+ height: 100%;float: left;
+ line-height: -25px;
+ cursor: pointer; margin: 10px; float: right;
+}
+
+/* testing embedded PHP */
+li {
+ background:url(<?php print $staticserver; ?>/images/<?php print $staticdir; ?>/bullet.gif) left <?php print $left; ?>px no-repeat; margin:0px; padding-left:10px; margin-bottom:<?php echo $marginBottom; ?>px; line-height:13px;
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr=#2e62a8, endColorstr=#123363);
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowMultipleStyleDefinitions 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowMultipleStyleDefinitionsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 5 => 2,
+ 10 => 4,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+.AssetLineageWidgetType-item {
+ color: #FFF;
+}
+
+.AssetLineageWidgetType-title {
+ color: #CCC;
+}
+
+.AssetLineageWidgetType-item {
+ color: #CCC;
+}
+
+.AssetLineageWidgetType-item .selected {
+}
+
+.AssetLineageWidgetType-item.selected {
+}
+
+#Blah .AssetLineageWidgetType-item {
+}
+
+#X.selected,
+.AssetLineageWidgetType-item {
+}
+
+.MyClass, .YourClass {
+}
+
+.YourClass, .MyClass {
+}
+
+.YourClass, .MyClass, .OurClass {
+}
+
+
+.ClassAtTopOfMediaBlock {
+}
+
+@media print {
+ .ClassAtTopOfMediaBlock {
+ }
+
+ .ClassInMultipleMediaBlocks {
+ }
+}
+
+.ClassNotAtTopOfMediaBlock {
+}
+
+@media handheld {
+ .SameClassInMediaBlock {
+ }
+
+ .ClassNotAtTopOfMediaBlock {
+ }
+
+ .SameClassInMediaBlock {
+ }
+}
+
+@media braille {
+ .PlaceholderClass {
+ }
+
+ .ClassNotAtTopOfMediaBlock {
+ }
+
+ .ClassInMultipleMediaBlocks {
+ }
+}
+
+.foo /* any comment */
+{ color: red; }
--- /dev/null
+<?php
+/**
+ * Unit test class for the DuplicateClassDefinition 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DuplicateClassDefinitionUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 9 => 1,
+ 29 => 1,
+ 57 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+.ViperSubToolbar-wrapper {
+ height: 34px;
+ left: 0;
+ position: fixed;
+ top: 60px;
+ z-index: 997;
+ left: 50%;
+}
+
+.expandable {
+ -moz-transition-property: margin-left, margin-right;
+ -moz-transition-duration: 0.2s;
+ -moz-transition-timing-function: ease;
+ -webkit-transition-property: margin-left, margin-right;
+ -webkit-transition-duration: 0.2s;
+ -webkit-transition-timing-function: ease;
+ z-index: 2;
+}
+
+@media only screen and (max-width: 480px) {
+ header nav.meta a { display: none; }
+ header nav.meta a.search { display: block; }
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the DuplicateStyleDefinition 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DuplicateStyleDefinitionUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(7 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+.HelpWidgetType-new-bug-title {}
+.HelpWidgetType-new-bug-title {
+}
+.HelpWidgetType-new-bug-title {
+
+}
+.HelpWidgetType-new-bug-title {
+
+}
+.HelpWidgetType-new-bug-title {
+ /* Nothing to see here */
+}
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the EmptyClassDefinition 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class EmptyClassDefinitionUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 1 => 1,
+ 2 => 1,
+ 4 => 1,
+ 7 => 1,
+ 10 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+#MetadataAdminScreen-addField-fieldType {
+ margin-left: 10px;
+ margin-right:
+ float: ;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the EmptyStyleDefinition 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class EmptyStyleDefinitionUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 4 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+#add-new-comment {
+ -moz-border-radius: 1px;
+ -webkit-border-radius: 1px;
+ border-radius: 1px;
+
+ -moz-border-radius-topleft: 1px;
+ -moz-border-radius-topright: 1px;
+ -moz-border-radius-bottomright: 1px;
+ -moz-border-radius-bottomleft: 1px;
+ border-top-left-radius: 1px;
+ border-top-right-radius: 1px;
+ border-bottom-right-radius: 1px;
+ border-bottom-left-radius: 1px;
+
+ -moz-box-shadow: 1px;
+ -webkit-box-shadow: 1px;
+ box-shadow: 1px;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ForbiddenStyles 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ForbiddenStylesUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 3 => 1,
+ 6 => 1,
+ 7 => 1,
+ 8 => 1,
+ 9 => 1,
+ 15 => 1,
+ 16 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+body {
+
+ font-family: Arial, Helvetica, sans-serif;
+ margin: 40px 0 0 0;
+padding: 0;
+ background: #8FB7DB url(diag_lines_bg.gif) top left;
+
+}
+
+td {
+ margin: 40px;
+
+ padding: 20px;
+}
+
+/*
+#AdminScreenModeWidgetType-tab_pane-containers .TabPaneWidgetType-tab-selected-left {
+ background: transparent url(images/ScreenImages/tab_on_left.png) no-repeat;
+}
+#AdminScreenModeWidgetType-tab_pane-containers .TabPaneWidgetType-tab-selected-right {
+ background: transparent url(images/ScreenImages/tab_on_right.png) no-repeat;
+}
+*/
+
+.GUITextBox.container:after {}
+
+@media screen and (max-device-width: 769px) {
+
+ header #logo img {
+ max-width: 100%;
+ padding: 20px;
+ margin: 40px;
+ }
+}
+
+@media screen and (max-device-width: 769px) {
+
+ header #logo img {
+ max-width: 100%;
+ }
+
+ header #logo img {
+ min-width: 100%;
+ }
+
+}
+
+td {
+ margin: 40px;
+
+ padding: 20px;
+
+
+}
+
+.GUIFileUpload {
+/* opacity: 0.25; */
+}
+
+.foo
+{
+ border: none;
+}
+
+.mortgage-calculator h2 {
+ background: #072237;
+ color: #fff;
+ font-weight: normal;
+ height: 50px;
+ line-height: 50px;
+ padding: 0 0 0 30px;
+ }
+
+/* syntax error */
+--------------------------------------------- */
--- /dev/null
+body {
+ font-family: Arial, Helvetica, sans-serif;
+ margin: 40px 0 0 0;
+ padding: 0;
+ background: #8FB7DB url(diag_lines_bg.gif) top left;
+}
+
+td {
+ margin: 40px;
+ padding: 20px;
+}
+
+/*
+#AdminScreenModeWidgetType-tab_pane-containers .TabPaneWidgetType-tab-selected-left {
+ background: transparent url(images/ScreenImages/tab_on_left.png) no-repeat;
+}
+#AdminScreenModeWidgetType-tab_pane-containers .TabPaneWidgetType-tab-selected-right {
+ background: transparent url(images/ScreenImages/tab_on_right.png) no-repeat;
+}
+*/
+
+.GUITextBox.container:after {}
+
+@media screen and (max-device-width: 769px) {
+
+ header #logo img {
+ max-width: 100%;
+ padding: 20px;
+ margin: 40px;
+ }
+}
+
+@media screen and (max-device-width: 769px) {
+
+ header #logo img {
+ max-width: 100%;
+ }
+
+ header #logo img {
+ min-width: 100%;
+ }
+
+}
+
+td {
+ margin: 40px;
+ padding: 20px;
+}
+
+.GUIFileUpload {
+/* opacity: 0.25; */
+}
+
+.foo
+{
+ border: none;
+}
+
+.mortgage-calculator h2 {
+ background: #072237;
+ color: #fff;
+ font-weight: normal;
+ height: 50px;
+ line-height: 50px;
+ padding: 0 0 0 30px;
+}
+
+/* syntax error */
+--------------------------------------------- */
--- /dev/null
+<?php
+/**
+ * Unit test class for the Indentation 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class IndentationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 3 => 1,
+ 5 => 1,
+ 6 => 1,
+ 7 => 1,
+ 12 => 1,
+ 30 => 1,
+ 32 => 1,
+ 50 => 1,
+ 52 => 1,
+ 53 => 1,
+ 66 => 1,
+ 67 => 1,
+ 68 => 1,
+ 69 => 1,
+ 70 => 1,
+ 71 => 1,
+ 72 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+.SettingsTabPaneWidgetType-tab-mid {
+ font-family: Arial;
+ Font-Family: arial;
+ background-color: #DA9393;
+ BACKGROUND-IMAGE: URL(Warning_Close.png);
+}
+
+@media screen and (max-device-width: 769px) {
+
+ .SettingsTabPaneWidgetType-tab-mid {
+ Font-Family: arial;
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr=#2e62a8, endColorstr=#123363);
+ }
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the LowercaseStyleDefinition 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LowercaseStyleDefinitionUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 3 => 1,
+ 5 => 2,
+ 11 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+.my-style {
+ margin-right 15px;
+ float: left;
+ margin-left 15px;
+ margin-top: 15px;
+ margin-bottom 15px;
+}
+
+@media screen and (max-device-width: 769px) {
+ header #logo img {
+ max-width: 100%;
+ margin-bottom 15px;
+ }
+}
+
+#foo { background-color: #FF0000;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the MissingColon 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class MissingColonUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 4 => 1,
+ 6 => 1,
+ 12 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+#red {
+ background-color: red;
+}
+
+.red {
+ border-bottom: 1px dotted black;
+ border-top: 1px dotted gray;
+}
+
+#red.yellow {
+ background: yellow url(diag_lines_bg.gif) top left;
+ text-shadow: 0 1px 0 white;
+}
+
+.something--white {
+ border: 0;
+}
+
+.something--------------white {
+ border: 0;
+}
+
+.-white {
+ border: 0;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the NamedColours 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class NamedColoursUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 6 => 1,
+ 7 => 1,
+ 11 => 1,
+ 12 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+.my-style {
+ opacity: 0;
+ opacity: 0.0;
+ opacity: 1;
+ opacity: 1.0;
+ opacity: 1.5;
+ opacity: .5;
+ opacity: 0.5;
+ opacity: 2;
+ opacity: -1;
+ opacity: 0.55;
+}
+
+div {
+ font-size: 1.2em;
+ background: linear-gradient(to bottom, #00F, #0F0) repeat scroll 50% 50% #EEE;
+ min-width: 250px;
+ max-width: 100%;
+ padding-bottom: 50px;
+ box-shadow: 2px -3px 3px rgba(100, 100, 100, 0.33);
+ border-left: 1px solid #000;
+ border-right: 1px solid #000;
+ border-top: 1px solid #000;
+ border-bottom: 1px dotted #000;
+ background: url(../1/2/3/4-5-6_7_100x100.png) repeat scroll 50% 50% #EEE;
+ opacity: -1;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the Opacity 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class OpacityUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 5 => 1,
+ 6 => 1,
+ 7 => 1,
+ 9 => 1,
+ 10 => 1,
+ 11 => 1,
+ 26 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+.HelpWidgetType-new-bug-title {
+ width: 308px
+ float: left;
+}
+
+#MetadataAdminScreen-addField-add {
+ float: left ;
+}
+
+.TableWidgetType .recover:hover {
+ background-color: #FFF;
+}
+
+#clearCache-settings:rootNodes-list_0 {
+ border-top: none;
+}
+
+.HelpWidgetType-list {
+ list-style-image: url();
+}
+
+@media (min-width: 320px) and (max-width: 961px) {
+ .tooltipsrt:hover span.tltp,
+ .tooltipstp:hover span.tltp {
+ visibility: hidden;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the SemicolonSpacing 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class SemicolonSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 7 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+#add-new-comment {
+ background-color: #333333;
+ padding: 10px;
+ border-bottom: 1px dotted #F0F0F0;
+ border-top: 1px dotted #FF00FF;
+ background: #8fb7db url(diag_lines_bg.gif) top left;
+ page-break-inside: 1;
+ margin: 8px 8px 8px 8px;
+ margin: 8px 8px;
+ margin: 0 0 0 0;
+ margin: 0 8px 0 8px;
+ margin: 8px 4px 8px 4px;
+ margin: 8px 4% 8px 4%;
+ margin: 6px 2px 9px 2px;
+ margin: 6px 2px 9px;
+ border-radius: 2px 2px 2px 2px !important;
+ border-width: 2px 2px 2px 2px;
+ border-width: 1px 2px 2px 4px;
+ margin: 97px auto 0 auto;
+ text-shadow: 0 1px 0 #fff;
+
+ /* These are black-listed style names. */
+ background-position: 0 0;
+ box-shadow: 2px 2px 2px 2px;
+ transform-origin: 0 110% 0;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ShorthandSize 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\Standards\Squiz\Tests\CSS;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ShorthandSizeUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 8 => 1,
+ 9 => 1,
+ 10 => 1,
+ 11 => 1,
+ 12 => 1,
+ 13 => 1,
+ 15 => 1,
+ 16 => 1,
+ 17 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+
+
+ class ClassDeclaration
+{
+
+}
+
+abstract class CorrectClassDeclarationWithExtends extends correctClassDeclaration
+{
+
+}
+
+final class CorrectClassDeclarationWithImplements implements correctClassDeclaration
+{
+
+}
+
+
+// Incorrect placement of opening/closing braces, including indent.
+class IncorrectBracePlacement {}
+class IncorrectBracePlacementWithExtends extends correctClassDeclaration {}
+class IncorrectBracePlacementWithImplements implements correctClassDeclaration {}
+
+
+ class IncorrectIndentedClass
+ {
+
+ }//end class
+
+
+// Incorrect code placement for opening/closing brace.
+class IncorrectCodeAfterOpeningBrace
+{ echo phpinfo();
+
+}//end class
+
+class IncorrectCodeAfterClosingBrace
+{
+
+} echo phpinfo();
+
+
+class IncorrectCodeBeforeClosingBrace
+{
+
+echo phpinfo(); }
+
+ class IncorrectIndentedClass
+{
+
+}
+
+class ClassOne implements ClassTwo, ClassThree
+{
+}//end class
+
+class ClassOne implements ClassFour ,ClassFive, ClassSix
+{
+}//end class
+
+class CorrectClassDeclarationWithExtends extends correctClassDeclaration
+
+{
+
+}
+
+class CorrectClassDeclarationWithExtends extends correctClassDeclaration
+{
+
+}//end class
+
+class CorrectClassDeclaration
+{
+
+}//end class
+
+
+class CorrectClassDeclaration extends CorrectClassDeclaration2 implements ICorrectClassDeclaration
+{
+
+}//end class
+
+class File implements \Zend_Auth_Storage_Interface,\Zend_Auth_Storage, \Zend_Foo
+{
+}
+
+interface MyInterface
+{
+
+}
+?>
+
+<?php
+
+class CorrectClassDeclaration
+{
+
+}//end class
+
+// Class comment here, but wrong comment type.
+class testing
+{
+} /* end class */ echo 'hi';
+
+class IncorrectCodeBeforeClosingBrace
+{
+
+echo phpinfo();}
--- /dev/null
+<?php
+
+
+
+class ClassDeclaration
+{
+
+}
+
+abstract class CorrectClassDeclarationWithExtends extends correctClassDeclaration
+{
+
+}
+
+final class CorrectClassDeclarationWithImplements implements correctClassDeclaration
+{
+
+}
+
+// Incorrect placement of opening/closing braces, including indent.
+class IncorrectBracePlacement
+{
+}
+
+class IncorrectBracePlacementWithExtends extends correctClassDeclaration
+{
+}
+
+class IncorrectBracePlacementWithImplements implements correctClassDeclaration
+{
+}
+
+class IncorrectIndentedClass
+{
+
+}//end class
+
+// Incorrect code placement for opening/closing brace.
+class IncorrectCodeAfterOpeningBrace
+{
+ echo phpinfo();
+
+}//end class
+
+class IncorrectCodeAfterClosingBrace
+{
+
+}
+
+echo phpinfo();
+
+
+class IncorrectCodeBeforeClosingBrace
+{
+
+echo phpinfo();
+}
+
+class IncorrectIndentedClass
+{
+
+}
+
+class ClassOne implements ClassTwo, ClassThree
+{
+}//end class
+
+class ClassOne implements ClassFour, ClassFive, ClassSix
+{
+}//end class
+
+class CorrectClassDeclarationWithExtends extends correctClassDeclaration
+{
+
+}
+
+class CorrectClassDeclarationWithExtends extends correctClassDeclaration
+{
+
+}//end class
+
+class CorrectClassDeclaration
+{
+
+}//end class
+
+class CorrectClassDeclaration extends CorrectClassDeclaration2 implements ICorrectClassDeclaration
+{
+
+}//end class
+
+class File implements \Zend_Auth_Storage_Interface, \Zend_Auth_Storage, \Zend_Foo
+{
+}
+
+interface MyInterface
+{
+
+}
+
+?>
+
+<?php
+
+class CorrectClassDeclaration
+{
+
+}//end class
+
+// Class comment here, but wrong comment type.
+class testing
+{
+} /* end class */
+
+echo 'hi';
+
+class IncorrectCodeBeforeClosingBrace
+{
+
+echo phpinfo();
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClassDeclaration 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\Standards\Squiz\Tests\Classes;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClassDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 5 => 1,
+ 6 => 1,
+ 10 => 1,
+ 15 => 2,
+ 18 => 1,
+ 22 => 4,
+ 23 => 4,
+ 24 => 4,
+ 27 => 2,
+ 30 => 2,
+ 34 => 1,
+ 35 => 1,
+ 39 => 1,
+ 42 => 1,
+ 45 => 1,
+ 48 => 1,
+ 50 => 2,
+ 51 => 1,
+ 55 => 1,
+ 59 => 4,
+ 63 => 1,
+ 65 => 1,
+ 69 => 3,
+ 74 => 2,
+ 77 => 1,
+ 80 => 1,
+ 85 => 3,
+ 89 => 1,
+ 92 => 1,
+ 97 => 1,
+ 103 => 1,
+ 105 => 1,
+ 107 => 1,
+ 110 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+
+// Valid class name (matching filename).
+class ClassFileNameUnitTest {}
+
+
+// Invalid filename matching class name (case sensitive).
+class classFileNameUnitTest {}
+class classfilenameunittest {}
+class CLASSFILENAMEUNITTEST {}
+
+
+// Invalid non-filename matching class names.
+class CompletelyWrongClassName {}
+class ClassFileNameUnitTestExtra {}
+class ClassFileNameUnitTestInc {}
+class ExtraClassFileNameUnitTest {}
+
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClassFileName 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\Standards\Squiz\Tests\Classes;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClassFileNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 9 => 1,
+ 10 => 1,
+ 11 => 1,
+ 15 => 1,
+ 16 => 1,
+ 17 => 1,
+ 18 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+var x = {
+ abc: 1,
+ zyz: 2,
+ abc: 5,
+ mno: {
+ abc: 4
+ },
+ abc: 5
+
+ this.request({
+ action: 'getSubmissions'
+ });
+
+ this.request({
+ action: 'deleteSubmission'
+ });
+}
+
+
+LinkingEditScreenWidgetType.prototype = {
+
+ _addDeleteButtonEvent: function(parentid)
+ {
+ var params = {
+ screen: 'LinkingEditScreenWidget',
+ assetid: self.assetid,
+ parentid: parentid,
+ assetid: parentid,
+ op: 'deleteLink'
+ };
+
+ },
+
+ saveDesignEdit: function()
+ {
+ var params = {
+ screen: [this.id, 'Widget'].join(''),
+ assetid: this.assetid,
+ changes: dfx.jsonEncode(this.currnetLinksWdgt.getChanges()),
+ op: 'saveLinkEdit'
+ };
+
+ }
+
+};
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the DuplicateProperty 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\Standards\Squiz\Tests\Classes;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DuplicatePropertyUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 4 => 1,
+ 8 => 1,
+ 28 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+Abstract Class MyClass Extends MyClass {}
+Final Class MyClass Implements MyInterface {}
+Interface MyInterface {}
+Trait MyTrait {}
+
+class MyClass
+{
+ Var $myVar = null;
+ Const myConst = true;
+}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the LowercaseClassKeywords 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\Standards\Squiz\Tests\Classes;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LowercaseClassKeywordsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ $errors = array(
+ 2 => 3,
+ 3 => 3,
+ 4 => 1,
+ 5 => 1,
+ 9 => 1,
+ 10 => 1,
+ );
+
+ return $errors;
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+
+class SelfMemberReferenceUnitTestExample
+{
+
+
+ private $testCount = 0;
+
+
+ private $testResults = array();
+
+
+ public function SelfMemberReferenceUnitTestExample()
+ {
+ $testResults =& $this->testResults;
+
+
+ // Correct call to self.
+ $testResults[] = self::selfMemberReferenceUnitTestFunction();
+ $testResults[] = parent::selfMemberReferenceUnitTestFunction();
+
+ // Incorrect case.
+ $testResults[] = Self::selfMemberReferenceUnitTestFunction();
+ $testResults[] = SELF::selfMemberReferenceUnitTestFunction();
+ $testResults[] = SelfMemberReferenceUnitTestExample::selfMemberReferenceUnitTestFunction();
+
+
+ // Incorrect spacing.
+ $testResults[] = self ::selfMemberReferenceUnitTestFunction();
+ $testResults[] = self:: selfMemberReferenceUnitTestFunction();
+ $testResults[] = self :: selfMemberReferenceUnitTestFunction();
+
+ }
+
+
+ function selfMemberReferenceUnitTestFunction()
+ {
+ $this->testCount = $this->testCount + 1;
+ return $this->testCount;
+
+ }
+
+
+}
+
+
+class MyClass {
+
+ public static function test($value) {
+ echo "$value\n";
+ }
+
+ public static function walk() {
+ $callback = function($value, $key) {
+ // This is valid because you cant use self:: in a closure
+ MyClass::test($value);
+ };
+
+ $array = array(1,2,3);
+ array_walk($array, $callback);
+ }
+}
+
+MyClass::walk();
+
+class Controller
+{
+ public function Action()
+ {
+ Doctrine\Common\Util\Debug::dump();
+ }
+}
+
+class Foo
+{
+ public static function bar()
+ {
+ \Foo::baz();
+ }
+}
+
+namespace TYPO3\CMS\Reports;
+
+class Status {
+ const NOTICE = -2;
+ const INFO = -1;
+ const OK = 0;
+ const WARNING = 1;
+ const ERROR = 2;
+}
+
+namespace TYPO3\CMS\Reports\Report\Status;
+
+class Status implements \TYPO3\CMS\Reports\ReportInterface {
+ public function getHighestSeverity(array $statusCollection) {
+ $highestSeverity = \TYPO3\CMS\Reports\Status::NOTICE;
+ }
+}
+
+namespace Foo;
+
+class Bar {
+
+ function myFunction()
+ {
+ \Foo\Whatever::something();
+ \Foo\Bar::something();
+ }
+}
+
+namespace Foo\Bar;
+
+class Baz {
+
+ function myFunction()
+ {
+ \Foo\Bar\Whatever::something();
+ \Foo\Bar\Baz::something();
+ }
+}
--- /dev/null
+<?php
+
+
+class SelfMemberReferenceUnitTestExample
+{
+
+
+ private $testCount = 0;
+
+
+ private $testResults = array();
+
+
+ public function SelfMemberReferenceUnitTestExample()
+ {
+ $testResults =& $this->testResults;
+
+
+ // Correct call to self.
+ $testResults[] = self::selfMemberReferenceUnitTestFunction();
+ $testResults[] = parent::selfMemberReferenceUnitTestFunction();
+
+ // Incorrect case.
+ $testResults[] = self::selfMemberReferenceUnitTestFunction();
+ $testResults[] = self::selfMemberReferenceUnitTestFunction();
+ $testResults[] = self::selfMemberReferenceUnitTestFunction();
+
+
+ // Incorrect spacing.
+ $testResults[] = self::selfMemberReferenceUnitTestFunction();
+ $testResults[] = self::selfMemberReferenceUnitTestFunction();
+ $testResults[] = self::selfMemberReferenceUnitTestFunction();
+
+ }
+
+
+ function selfMemberReferenceUnitTestFunction()
+ {
+ $this->testCount = $this->testCount + 1;
+ return $this->testCount;
+
+ }
+
+
+}
+
+
+class MyClass {
+
+ public static function test($value) {
+ echo "$value\n";
+ }
+
+ public static function walk() {
+ $callback = function($value, $key) {
+ // This is valid because you cant use self:: in a closure
+ MyClass::test($value);
+ };
+
+ $array = array(1,2,3);
+ array_walk($array, $callback);
+ }
+}
+
+MyClass::walk();
+
+class Controller
+{
+ public function Action()
+ {
+ Doctrine\Common\Util\Debug::dump();
+ }
+}
+
+class Foo
+{
+ public static function bar()
+ {
+ self::baz();
+ }
+}
+
+namespace TYPO3\CMS\Reports;
+
+class Status {
+ const NOTICE = -2;
+ const INFO = -1;
+ const OK = 0;
+ const WARNING = 1;
+ const ERROR = 2;
+}
+
+namespace TYPO3\CMS\Reports\Report\Status;
+
+class Status implements \TYPO3\CMS\Reports\ReportInterface {
+ public function getHighestSeverity(array $statusCollection) {
+ $highestSeverity = \TYPO3\CMS\Reports\Status::NOTICE;
+ }
+}
+
+namespace Foo;
+
+class Bar {
+
+ function myFunction()
+ {
+ \Foo\Whatever::something();
+ self::something();
+ }
+}
+
+namespace Foo\Bar;
+
+class Baz {
+
+ function myFunction()
+ {
+ \Foo\Bar\Whatever::something();
+ self::something();
+ }
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the SelfMemberReference 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\Standards\Squiz\Tests\Classes;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class SelfMemberReferenceUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 24 => 1,
+ 25 => 1,
+ 26 => 1,
+ 30 => 1,
+ 31 => 1,
+ 32 => 2,
+ 79 => 1,
+ 108 => 1,
+ 119 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+
+// Valid class name.
+class ValidCamelCaseClass extends MyClass {}
+
+
+// Incorrect usage of camel case.
+class invalidCamelCaseClass extends MyClass {}
+class Invalid_Camel_Case_Class_With_Underscores implements MyClass {}
+
+
+// All lowercase.
+class invalidlowercaseclass extends MyClass {}
+class invalid_lowercase_class_with_underscores extends MyClass {}
+
+
+// All uppercase.
+class VALIDUPPERCASECLASS extends MyClass {}
+class INVALID_UPPERCASE_CLASS_WITH_UNDERSCORES extends MyClass {}
+
+
+// Mix camel case with uppercase.
+class ValidCamelCaseClassWithUPPERCASE extends MyClass {}
+
+
+// Usage of numeric characters.
+class ValidCamelCaseClassWith1Number extends MyClass {}
+class ValidCamelCaseClassWith12345Numbers extends MyClass {}
+class 5InvalidCamelCaseClassStartingWithNumber extends MyClass {}
+class ValidCamelCaseClassEndingWithNumber5 extends MyClass {}
+class 12345 extends MyClass {}
+
+class Testing{}
+
+class Base
+{
+ protected $anonymous;
+
+ public function __construct()
+ {
+ $this->anonymous = new class extends ArrayObject
+ {
+ public function __construct()
+ {
+ parent::__construct(['a' => 1, 'b' => 2]);
+ }
+ };
+ }
+}
+
+// Valid interface name.
+interface ValidCamelCaseClass extends MyClass {}
+
+
+// Incorrect usage of camel case.
+interface invalidCamelCaseClass extends MyClass {}
+interface Invalid_Camel_Case_Class_With_Underscores implements MyClass {}
+
+
+// All lowercase.
+interface invalidlowercaseclass extends MyClass {}
+interface invalid_lowercase_class_with_underscores extends MyClass {}
+
+
+// All uppercase.
+interface VALIDUPPERCASECLASS extends MyClass {}
+interface INVALID_UPPERCASE_CLASS_WITH_UNDERSCORES extends MyClass {}
+
+
+// Mix camel case with uppercase.
+interface ValidCamelCaseClassWithUPPERCASE extends MyClass {}
+
+
+// Usage of numeric characters.
+interface ValidCamelCaseClassWith1Number extends MyClass {}
+interface ValidCamelCaseClassWith12345Numbers extends MyClass {}
+interface 5InvalidCamelCaseClassStartingWithNumber extends MyClass {}
+interface ValidCamelCaseClassEndingWithNumber5 extends MyClass {}
+interface 12345 extends MyClass {}
+
+interface Testing{}
+
+interface Base
+{
+ protected $anonymous;
+
+ public function __construct();
+}
+
+
+// Valid trait name.
+trait ValidCamelCaseClass extends MyClass {}
+
+
+// Incorrect usage of camel case.
+trait invalidCamelCaseClass extends MyClass {}
+trait Invalid_Camel_Case_Class_With_Underscores implements MyClass {}
+
+
+// All lowercase.
+trait invalidlowercaseclass extends MyClass {}
+trait invalid_lowercase_class_with_underscores extends MyClass {}
+
+
+// All uppercase.
+trait VALIDUPPERCASECLASS extends MyClass {}
+trait INVALID_UPPERCASE_CLASS_WITH_UNDERSCORES extends MyClass {}
+
+
+// Mix camel case with uppercase.
+trait ValidCamelCaseClassWithUPPERCASE extends MyClass {}
+
+
+// Usage of numeric characters.
+trait ValidCamelCaseClassWith1Number extends MyClass {}
+trait ValidCamelCaseClassWith12345Numbers extends MyClass {}
+trait 5InvalidCamelCaseClassStartingWithNumber extends MyClass {}
+trait ValidCamelCaseClassEndingWithNumber5 extends MyClass {}
+trait 12345 extends MyClass {}
+
+trait Testing{}
+
+trait Base
+{
+ protected $anonymous;
+
+ public function __construct()
+ {
+ $this->anonymous = new class extends ArrayObject
+ {
+ public function __construct()
+ {
+ parent::__construct(['a' => 1, 'b' => 2]);
+ }
+ };
+ }
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ValidClassName 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\Standards\Squiz\Tests\Classes;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ValidClassNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 9 => 1,
+ 10 => 1,
+ 14 => 1,
+ 15 => 1,
+ 20 => 1,
+ 30 => 1,
+ 32 => 1,
+ 57 => 1,
+ 58 => 1,
+ 62 => 1,
+ 63 => 1,
+ 68 => 1,
+ 78 => 1,
+ 80 => 1,
+ 97 => 1,
+ 98 => 1,
+ 102 => 1,
+ 103 => 1,
+ 108 => 1,
+ 118 => 1,
+ 120 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+/*
+ Comments need to be indented 4 space.
+*/
+
+/*
+ same with multi-line
+ comments.
+*/
+
+/*
+ But they can:
+ - be indented more than 4
+ - but not less than 4
+*/
+
+/*
+ - This is valid:
+ Not indented correctly.
+*/
+
+/*
+ Comments need to be indented 4 space.
+*/
+
+/*
+ Comments need to be indented 4 space.
+ Comments need to be indented 4 space.
+ Comments need to be indented 4 space.
+ Comments need to be indented 4 space.
+
+ Comments need to be indented 4 space.
+ Comments need to be indented 4 space.
+*/
+
+/*
+ Block comments require a blank
+ line after them.
+*/
+$code = 'should not be here';
+
+/*
+ Closing comment not aligned
+ */
+
+/*
+ Closing comment not aligned
+ */
+
+/* Not allowed */
+
+/* Not allowed
+ either.
+*/
+
+/* */
+
+$code = 'should not be here';
+/*
+
+ Block comments require a blank
+ line before them. */
+
+/*
+*/
+
+/** Not allowed */
+
+/** Not allowed
+ either.
+**/
+
+/*
+ no capital
+ letter.
+*/
+
+echo 'hi';
+
+function func()
+{
+ echo 1;
+ /**
+ test
+ here
+ **/
+ echo 'test';
+ /**
+ Test
+ here
+ **/
+
+}//end func()
+
+public static function test()
+{
+ /*
+ Block comments do not require a blank line before them
+ if they are after T_OPEN_CURLY_BRACKET.
+ */
+
+ $code = '';
+
+}//end test()
+
+
+public static function test()
+{
+
+ /*
+ Block comments do not require a blank line before them
+ if they are after T_OPEN_CURLY_BRACKET.
+ */
+
+ $code = '';
+
+}//end test()
+
+class MyClass
+{
+
+ /**
+ * Comment should be ignored.
+ *
+ * @var integer
+ * @since 4.0.0
+ */
+ const LEFT = 1;
+
+}
+
+/**
+ * Comment should be ignored.
+ *
+ */
+final class MyClass
+{
+ /**
+ * Comment should be ignored.
+ *
+ */
+ final public function test() {}
+}
+
+switch ($var) {
+ case 'foo':
+ /*
+ Foo comment.
+ This is a multiple
+ line comment for Foo.
+ */
+
+ echo 'Foo';
+ break;
+
+ default:
+
+ /*
+ Foo comment.
+ This is a multiple
+ line comment for Foo.
+ */
+
+ echo 'Default';
+ break;
+}//end switch
+
+/**
+ * Comment should be ignored in PHP < 5.4.
+ *
+ */
+trait MyTrait {
+
+}
+
+/*
+ 这是一条测试评论.
+*/
+
+/*Channels::includeSystem('Permission');
+if (Permission::hasPermission($projectid, 'project.metadata.add') === FALSE) {
+ throw new PermissionException(_('You do not have permission to add metadata field'));
+}*/
+
+/*
+ Comment goes here
+*/
+$two = (1 + 1); // I'm not a comment closer!
+
+class Foo
+{
+
+ /**
+ * Comment here.
+ *
+ * @var array
+ */
+ var $bar = array();
+
+}
+
+class TabTest {
+ public function bar() {
+ /*
+ * Comment line 1.
+ * Comment line 2.
+ */
+
+ if ($foo) {
+ echo 'foo';
+ }
+
+ /* Comment line 1.
+ Comment line 2.
+ */
+
+ if ($foo) {
+ echo 'foo';
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ Comments need to be indented 4 space.
+*/
+
+/*
+ same with multi-line
+ comments.
+*/
+
+/*
+ But they can:
+ - be indented more than 4
+ - but not less than 4
+*/
+
+/*
+ - This is valid:
+ Not indented correctly.
+*/
+
+/*
+ Comments need to be indented 4 space.
+*/
+
+/*
+ Comments need to be indented 4 space.
+ Comments need to be indented 4 space.
+ Comments need to be indented 4 space.
+ Comments need to be indented 4 space.
+
+ Comments need to be indented 4 space.
+ Comments need to be indented 4 space.
+*/
+
+/*
+ Block comments require a blank
+ line after them.
+*/
+$code = 'should not be here';
+
+/*
+ Closing comment not aligned
+ */
+
+/*
+ Closing comment not aligned
+ */
+
+// Not allowed
+
+
+/*
+ Not allowed
+ either.
+*/
+
+
+
+$code = 'should not be here';
+/*
+ Block comments require a blank
+ line before them. */
+
+
+
+// Not allowed
+
+
+/*
+ Not allowed
+ either.
+*/
+
+/*
+ no capital
+ letter.
+*/
+
+echo 'hi';
+
+function func()
+{
+ echo 1;
+ /*
+ test
+ here
+ */
+ echo 'test';
+ /*
+ Test
+ here
+ */
+
+}//end func()
+
+public static function test()
+{
+ /*
+ Block comments do not require a blank line before them
+ if they are after T_OPEN_CURLY_BRACKET.
+ */
+
+ $code = '';
+
+}//end test()
+
+
+public static function test()
+{
+
+ /*
+ Block comments do not require a blank line before them
+ if they are after T_OPEN_CURLY_BRACKET.
+ */
+
+ $code = '';
+
+}//end test()
+
+class MyClass
+{
+
+ /**
+ * Comment should be ignored.
+ *
+ * @var integer
+ * @since 4.0.0
+ */
+ const LEFT = 1;
+
+}
+
+/**
+ * Comment should be ignored.
+ *
+ */
+final class MyClass
+{
+ /**
+ * Comment should be ignored.
+ *
+ */
+ final public function test() {}
+}
+
+switch ($var) {
+ case 'foo':
+ /*
+ Foo comment.
+ This is a multiple
+ line comment for Foo.
+ */
+
+ echo 'Foo';
+ break;
+
+ default:
+
+ /*
+ Foo comment.
+ This is a multiple
+ line comment for Foo.
+ */
+
+ echo 'Default';
+ break;
+}//end switch
+
+/**
+ * Comment should be ignored in PHP < 5.4.
+ *
+ */
+trait MyTrait {
+
+}
+
+/*
+ 这是一条测试评论.
+*/
+
+/*
+ Channels::includeSystem('Permission');
+ if (Permission::hasPermission($projectid, 'project.metadata.add') === FALSE) {
+ throw new PermissionException(_('You do not have permission to add metadata field'));
+}*/
+
+/*
+ Comment goes here
+*/
+$two = (1 + 1); // I'm not a comment closer!
+
+class Foo
+{
+
+ /**
+ * Comment here.
+ *
+ * @var array
+ */
+ var $bar = array();
+
+}
+
+class TabTest {
+ public function bar() {
+ /*
+ * Comment line 1.
+ * Comment line 2.
+ */
+
+ if ($foo) {
+ echo 'foo';
+ }
+
+ /*
+ Comment line 1.
+ Comment line 2.
+ */
+
+ if ($foo) {
+ echo 'foo';
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the BlockComment 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\Standards\Squiz\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class BlockCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Get a list of CLI values to set before the file is tested.
+ *
+ * @param string $testFile The name of the file being tested.
+ * @param \PHP_CodeSniffer\Config $config The config data for the test run.
+ *
+ * @return void
+ */
+ public function setCliValues($testFile, $config)
+ {
+ $config->tabWidth = 4;
+
+ }//end setCliValues()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ $errors = array(
+ 8 => 1,
+ 20 => 1,
+ 24 => 1,
+ 30 => 1,
+ 31 => 1,
+ 34 => 1,
+ 40 => 1,
+ 45 => 1,
+ 49 => 1,
+ 51 => 1,
+ 53 => 1,
+ 57 => 1,
+ 60 => 1,
+ 61 => 1,
+ 63 => 1,
+ 65 => 1,
+ 68 => 1,
+ 70 => 1,
+ 72 => 1,
+ 75 => 1,
+ 84 => 1,
+ 87 => 1,
+ 89 => 1,
+ 92 => 1,
+ 111 => 1,
+ 159 => 1,
+ 181 => 1,
+ 188 => 1,
+ 206 => 1,
+ 207 => 1,
+ 214 => 1,
+ );
+
+ return $errors;
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+/** Class doc comment */
+
+class Comment
+{
+
+}//end class
+
+
+//
+// Sample class comment
+//
+//
+//
+class Invalid_Comment_Style1
+{
+
+}//end class
+
+/**
+ *
+ *
+ * Sample class comment.
+ *
+ *
+ * Long description with extra blank line before and after
+ *
+ *
+ * @version Release: 1.0
+ * @link http://pear.php.net/package/PHP_CodeSniffer
+ */
+
+class Extra_Description_Newlines
+{
+
+}//end class
+
+
+/**
+ * Sample class comment...
+ */
+class Missing_Newlines_Before_Tags
+{
+
+}//end class
+
+/**
+ * Simple class comment
+ *
+ * @deprecated asd
+ */
+class Checking_Tags
+{
+ class Sub_Class {
+
+ }//end class
+
+
+}//end class
+
+/**
+ * Simple class comment
+ * that spans multiple line
+ * and not end with a full stop
+ *
+ * @hello Hello tag
+ * @package phpcs
+ */
+class Unknown_Hello_Tag
+{
+
+}//end class
+
+/**
+ *
+ *
+ *
+ */
+class Empty_Class_Doc
+{
+
+}//end class
+
+/**
+ */
+class Missing_Short_Desc
+{
+
+}//end class
+
+/**
+ * sample class comment.
+ *
+ * - long description with extra blank line before and after
+ */
+class Incorrect_Case
+{
+
+}//end class
+
+/**
+ * 0sample class comment.
+ *
+ * long description with extra blank line before and after
+ */
+class Incorrect_Case2
+{
+
+}//end class
+
+/** Sample class comment.
+ */
+class Start_Tag_Incorrect
+{
+
+}//end class
+
+/**
+ * 这是一条测试评论.
+ *
+ */
+class Space_At_end
+{
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClassComment 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\Standards\Squiz\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClassCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 15 => 1,
+ 31 => 1,
+ 54 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 29 => 1,
+ 30 => 1,
+ 50 => 1,
+ 66 => 1,
+ 67 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+function __construct()
+{
+}//end __construct()
+
+function myFunction()
+{
+}//end myFunction()
+
+function ourFunction()
+{
+}//end ourFunction
+
+function yourFunction()
+{
+}//end myFunction()
+
+class TestClass
+{
+ function __construct()
+ {
+ }//end __construct()
+
+ function myFunction()
+ {
+ }//end myFunction()
+
+ function yourFunction()
+ {
+ }//end myFunction()
+
+}//end class
+
+abstract class TestClass
+{
+ abstract function myFunction();
+
+ function ourFunction()
+ {
+ }//end myFunction()
+
+ function yourFunction()
+ {
+ }//end yourFunction()
+
+}//end class
+
+interface TestClass
+{
+ function myFunction();
+ function ourFunction();
+ function yourFunction();
+
+}//end interface
+
+class TestClass
+{
+}
+
+abstract class TestClass
+{
+}
+
+interface TestClass
+{
+}
+
+class MyClass
+{
+ public function myFunction();
+}//end class
+
+// Closures don't need end comments.
+echo preg_replace_callback('~-([a-z])~', function ($match) { return strtoupper($match[1]); }, 'hello-world');
+
+class TestClass
+{
+}
+//end class
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClosingDeclarationComment 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\Standards\Squiz\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClosingDeclarationCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 13 => 1,
+ 17 => 1,
+ 31 => 1,
+ 41 => 1,
+ 59 => 1,
+ 63 => 1,
+ 67 => 1,
+ 79 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(71 => 1);
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+* Some info about the class here
+ *
+ */
+class MyClass
+{
+ /**
+ * Some info about the function here.
+ *
+ *@return void
+ */
+ function myFunction() {}
+}
+
+/**
+ * Some info about the class here
+ *
+ */
+class MyClass
+{
+ /**
+ *Some info about the function here.
+ *
+ * @return void
+ */
+ function myFunction() {}
+}
+
+/**
+ * Some info about the class here
+ *
+*/
+class MyClass
+{
+ /**
+ * Some info about the function here.
+ *
+ * @return void
+ */
+ function myFunction() {}
+}
+
+/** @var Database $mockedDatabase */
+/** @var Container $mockedContainer */
+
+function myFunction()
+{
+ echo 'hi';
+ /**
+ Comment here.
+ */
+}
+
+/**
+ * Creates a map of tokens => line numbers for each token.
+ *
+ * Long description with some points:
+ * - one
+ * - two
+ * - three
+ *
+ * @param array &$tokens The array of tokens to process.
+ * @param object $tokenizer The tokenizer being used to
+ * process this file.
+ * @param string $eolChar The EOL character to use for splitting strings.
+ *
+ * @return void
+ */
+function myFunction() {}
+
+class MyClass2
+{
+ /**
+ * Some info about the variable here.
+ */
+ var $x;
+}
--- /dev/null
+<?php
+/**
+ * Some info about the class here
+ *
+ */
+class MyClass
+{
+ /**
+ * Some info about the function here.
+ *
+ * @return void
+ */
+ function myFunction() {}
+}
+
+/**
+ * Some info about the class here
+ *
+ */
+class MyClass
+{
+ /**
+ * Some info about the function here.
+ *
+ * @return void
+ */
+ function myFunction() {}
+}
+
+/**
+ * Some info about the class here
+ *
+ */
+class MyClass
+{
+ /**
+ * Some info about the function here.
+ *
+ * @return void
+ */
+ function myFunction() {}
+}
+
+/** @var Database $mockedDatabase */
+/** @var Container $mockedContainer */
+
+function myFunction()
+{
+ echo 'hi';
+ /**
+ Comment here.
+ */
+}
+
+/**
+ * Creates a map of tokens => line numbers for each token.
+ *
+ * Long description with some points:
+ * - one
+ * - two
+ * - three
+ *
+ * @param array &$tokens The array of tokens to process.
+ * @param object $tokenizer The tokenizer being used to
+ * process this file.
+ * @param string $eolChar The EOL character to use for splitting strings.
+ *
+ * @return void
+ */
+function myFunction() {}
+
+class MyClass2
+{
+ /**
+ * Some info about the variable here.
+ */
+ var $x;
+}
--- /dev/null
+
+/**
+* Some info about the class here
+ *
+ */
+foo.prototype = {
+
+ /**
+ * Some info about the function here.
+ *
+ *@return void
+ */
+ bar: function() {}
+}
+
+/**
+ * Some info about the class here
+ *
+ */
+foo.prototype = {
+
+ /**
+ *Some info about the function here.
+ *
+ * @return void
+ */
+ bar: function() {}
+}
+
+/**
+ * Some info about the class here
+ *
+*/
+foo.prototype = {
+
+ /**
+ * Some info about the function here.
+ *
+ * @return void
+ */
+ bar: function() {}
+}
+
+/** @var Database $mockedDatabase */
+/** @var Container $mockedContainer */
+
+function myFunction()
+{
+ console.info('hi');
+ /**
+ Comment here.
+ */
+}
+
+/**
+ * Creates a map of tokens => line numbers for each token.
+ *
+ * Long description with some points:
+ * - one
+ * - two
+ * - three
+ *
+ * @param array &$tokens The array of tokens to process.
+ * @param object $tokenizer The tokenizer being used to
+ * process this file.
+ * @param string $eolChar The EOL character to use for splitting strings.
+ *
+ * @return void
+ */
+function myFunction() {}
+
+$.extend(Datepicker.prototype, {
+ _widgetDatepicker: function() {
+ },
+ /* Action for selecting a new month/year. */
+});
--- /dev/null
+
+/**
+ * Some info about the class here
+ *
+ */
+foo.prototype = {
+
+ /**
+ * Some info about the function here.
+ *
+ * @return void
+ */
+ bar: function() {}
+}
+
+/**
+ * Some info about the class here
+ *
+ */
+foo.prototype = {
+
+ /**
+ * Some info about the function here.
+ *
+ * @return void
+ */
+ bar: function() {}
+}
+
+/**
+ * Some info about the class here
+ *
+ */
+foo.prototype = {
+
+ /**
+ * Some info about the function here.
+ *
+ * @return void
+ */
+ bar: function() {}
+}
+
+/** @var Database $mockedDatabase */
+/** @var Container $mockedContainer */
+
+function myFunction()
+{
+ console.info('hi');
+ /**
+ Comment here.
+ */
+}
+
+/**
+ * Creates a map of tokens => line numbers for each token.
+ *
+ * Long description with some points:
+ * - one
+ * - two
+ * - three
+ *
+ * @param array &$tokens The array of tokens to process.
+ * @param object $tokenizer The tokenizer being used to
+ * process this file.
+ * @param string $eolChar The EOL character to use for splitting strings.
+ *
+ * @return void
+ */
+function myFunction() {}
+
+$.extend(Datepicker.prototype, {
+ _widgetDatepicker: function() {
+ },
+ /* Action for selecting a new month/year. */
+});
--- /dev/null
+<?php
+/**
+ * Unit test class for the DocCommentAlignment 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\Standards\Squiz\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DocCommentAlignmentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='DocCommentAlignmentUnitTest.inc')
+ {
+ $errors = array(
+ 3 => 1,
+ 11 => 1,
+ 17 => 1,
+ 18 => 1,
+ 19 => 1,
+ 23 => 2,
+ 24 => 1,
+ 25 => 2,
+ 26 => 1,
+ 32 => 1,
+ 33 => 1,
+ 38 => 1,
+ 39 => 1,
+ );
+
+ if ($testFile === 'DocCommentAlignmentUnitTest.inc') {
+ $errors[75] = 1;
+ }
+
+ return $errors;
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+try {
+ // Try something.
+ $variable = 'string';
+} catch (Exception $e) {
+ // Comment.
+ echo 'something broke';
+}
+
+try {
+ // Try something.
+ $variable = 'string';
+} catch (Exception $e) {
+}
+
+try {
+ // Try something.
+ $variable = 'string';
+} catch (Exception $e) {
+ // Dont want to do anything.
+}
+
+try {
+ $variable = 'string';
+} catch (MyException $e) {
+ echo 'something broke';
+} catch (Exception $e) {
+ echo 'something broke';
+}
+
+try {
+ $variable = 'string';
+} catch (MyException $e) {
+
+} catch (Exception $e) {
+ echo 'something broke';
+}
+
+try {
+ $variable = 'string';
+} catch (MyException $e) {
+ // Dont do anything.
+} catch (Exception $e) {
+ // Do nothing.
+}
+
+try {
+ $variable = 'string';
+} catch (MyException $e) {
+} catch (YourException $e) {
+} catch (OurException $e) {
+} catch (Exception $e) {
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the EmptyCatchComment 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\Standards\Squiz\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class EmptyCatchCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 13 => 1,
+ 33 => 1,
+ 49 => 1,
+ 50 => 1,
+ 51 => 1,
+ 52 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * File comment.
+ *
+ * @package Package
+ * @subpackage Subpackage
+ * @author Squiz Pty Ltd <products@squiz.net>
+ * @copyright 2010-2014 Squiz Pty Ltd (ABN 77 084 670 600)
+ */
+
+echo 'hi';
\ No newline at end of file
--- /dev/null
+/**
+ * File comment.
+ *
+ * @package Package
+ * @subpackage Subpackage
+ * @author Squiz Pty Ltd <products@squiz.net>
+ * @copyright 2010-2014 Squiz Pty Ltd (ABN 77 084 670 600)
+ */
+
+print 'hi';
\ No newline at end of file
--- /dev/null
+<?php
+
+
+
+/**
+*
+* 0Multi-line short description without full stop
+*
+*
+* asdasd
+* long description for file (if any)
+* asdasdadada
+*
+* PHP versio
+*
+* LICENSE: This source file is subject to version 3.0 of the PHP license
+* that is available through the world-wide-web at the following URI:
+* http://www.php.net/license/3_0.txt. If you did not receive a copy of
+* the PHP License and are unable to obtain it through the web, please
+* send a note to license@php.net so we can mail you a copy immediately.
+* @package SquizCMS
+* @package ADDITIONAL PACKAGE TAG
+* @subpkg not_camelcased
+* @author Antônio Carlos Venâncio Júnior <foreign@character.net>
+* @author
+* @copyright 1997~1994 The PHP Group
+* @copyright 1994-1997 The PHP Group
+* @copyright The PHP Group
+* @license http://www.php.net/license/3_0.txt
+* @summary An unknown summary tag
+*
+*/
+
+
+?>
+<?php
+/**
+* This bit here is not qualified as file comment
+*
+* as it is not after the first open tag
+*
+*/
+?>
--- /dev/null
+
+
+
+
+/**
+*
+* 0Multi-line short description without full stop
+*
+*
+* asdasd
+* long description for file (if any)
+* asdasdadada
+*
+* PHP versio
+*
+* LICENSE: This source file is subject to version 3.0 of the PHP license
+* that is available through the world-wide-web at the following URI:
+* http://www.php.net/license/3_0.txt. If you did not receive a copy of
+* the PHP License and are unable to obtain it through the web, please
+* send a note to license@php.net so we can mail you a copy immediately.
+* @package SquizCMS
+* @package ADDITIONAL PACKAGE TAG
+* @subpkg not_camelcased
+* @author Antônio Carlos Venâncio Júnior <foreign@character.net>
+* @author
+* @copyright 1997~1994 The PHP Group
+* @copyright 1994-1997 The PHP Group
+* @copyright The PHP Group
+* @license http://www.php.net/license/3_0.txt
+* @summary An unknown summary tag
+*
+*/
+
+
+/**
+* This bit here is not qualified as file comment
+*
+* as it is not the first comment in the file
+*
+*/
--- /dev/null
+<?php
+/**
+ * Unit test class for the FileComment 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\Standards\Squiz\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FileCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='FileCommentUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'FileCommentUnitTest.inc':
+ case 'FileCommentUnitTest.js':
+ return array(
+ 1 => 1,
+ 22 => 2,
+ 23 => 1,
+ 24 => 2,
+ 25 => 2,
+ 26 => 1,
+ 27 => 2,
+ 28 => 2,
+ 32 => 2,
+ );
+ default:
+ return array();
+ }
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class FunctionCommentThrowTagUnitTest
+{
+
+
+ /**
+ * Missing throw tag.
+ *
+ */
+ public function missingThrowTag()
+ {
+ throw new PHP_Exception('Error');
+
+ }//end missingThrowTag()
+
+
+ /**
+ * Tag and token number mismatch.
+ *
+ * @throws PHP_Exception1
+ */
+ public function oneMoreThrowsTagNeeded()
+ {
+ throw new PHP_Exception1('Error');
+ throw new PHP_Exception2('Error');
+
+ }//end oneMoreThrowsTagNeeded()
+
+
+ /**
+ * Tag and token number mismatch.
+ *
+ * @throws PHP_Exception1
+ * @throws PHP_Exception2
+ */
+ public function oneLessThrowsTagNeeded()
+ {
+ throw new PHP_Exception1('Error');
+
+ }//end oneLessThrowsTagNeeded()
+
+
+ /**
+ * Wrong exception type name.
+ *
+ * @throws PHP_Wrong_Exception
+ */
+ public function wrongExceptionName()
+ {
+ throw new PHP_Correct_Exception('Error');
+
+ }//end wrongExceptionName()
+
+
+ /**
+ * Wrong exception type name.
+ *
+ * @throws PHP_Correct_Exception1
+ * @throws PHP_Wrong_Exception2
+ * @throws PHP_Wrong_Exception3
+ */
+ public function moreWrongExceptionName()
+ {
+ throw new PHP_Correct_Exception1('Error');
+ throw new PHP_Correct_Exception2('Error');
+ throw new PHP_Correct_Exception3('Error');
+
+ }//end moreWrongExceptionName()
+
+
+ /**
+ * Wrong exception type name.
+ *
+ * @throws PHP_Correct_Exception
+ */
+ public function sameExceptionName()
+ {
+ throw new PHP_Correct_Exception('Error');
+ throw new PHP_Correct_Exception('Error');
+
+ }//end sameExceptionName()
+
+
+ /**
+ * This is a valid chunk.
+ *
+ * @throws PHP_Exception1
+ * @throws PHP_Exception2
+ */
+ public function thisShouldWorkOK()
+ {
+ throw new PHP_Exception2('Error');
+ throw new PHP_Exception1('Error');
+ throw new PHP_Exception2('Error');
+ throw new PHP_Exception1('Error');
+ throw new PHP_Exception2('Error');
+
+ }//end thisShouldWorkOK()
+
+
+ /**
+ * This is not OK, missing 2 @throws tag.
+ *
+ * @throws PHP_Exception1
+ * @throws PHP_Exception2
+ */
+ public function notOK()
+ {
+ throw new PHP_Missing_Exception1('Error');
+ throw new PHP_Exception2('Error');
+ throw new PHP_Missing_Exception2('Error');
+ throw new PHP_Missing_Exception2('Error');
+ throw new PHP_Exception1('Error');
+ throw new PHP_Exception2('Error');
+ throw new PHP_Missing_Exception1('Error');
+
+ }//end notOK()
+
+
+ /**
+ * Needs at least 1 throws tag, even though we
+ * don't know what it is.
+ */
+ public function notOKVariable()
+ {
+ try {
+ // Do something.
+ } catch (PHP_Exception2 $e) {
+ logError();
+ throw $e;
+ }
+
+ }//end notOKVariable()
+
+
+ /**
+ * Needs at least 1 throws tag, even though we
+ * don't know what it is.
+ *
+ * @throws PHP_Exception1
+ */
+ public function okVariable()
+ {
+ throw new PHP_Exception1('Error');
+
+ try {
+ // Do something.
+ } catch (PHP_Exception1 $e) {
+ logError();
+ throw $e;
+ }
+
+ }//end okVariable()
+
+
+ /**
+ * Needs 1 @throws tag.
+ *
+ * @throws Exception Unknown exception type.
+ */
+ function okVariable()
+ {
+ throw $e;
+
+ }//end okVariable()
+
+
+ /**
+ * Needs 1 @throws tag.
+ *
+ * @throws Exception Unknown exception type.
+ */
+ function okFunction()
+ {
+ throw $this->callSomeFunction();
+
+ }//end okFunction()
+
+
+ /**
+ * Comment inside function.
+ *
+ * @throws Exception
+ */
+ function okFunction()
+ {
+ /**
+ * @var FooClass
+ */
+ $foo = FooFactory::factory();
+ throw new Exception;
+
+ }//end okFunction
+
+ /**
+ * Needs at throws tag for rethrown exception,
+ * even though we have one throws tag.
+ *
+ * @throws PHP_Exception1
+ */
+ public function notOkVariableRethrown()
+ {
+ throw new PHP_Exception1('Error');
+
+ try {
+ // Do something.
+ } catch (PHP_Exception2 $e) {
+ logError();
+ throw $e;
+ }
+
+ }//end notOkVariableRethrown()
+
+ /**
+ * Needs at throws tag for rethrown exception,
+ * even though we have one throws tag.
+ *
+ * @throws PHP_Exception1
+ */
+ public function notOkVariableRethrown()
+ {
+ throw new PHP_Exception1('Error');
+
+ try {
+ // Do something.
+ } catch (PHP_Exception1 | PHP_Exception2 $e) {
+ logError();
+ throw $e;
+ }
+
+ }//end notOkVariableRethrown()
+
+ /**
+ * Has correct throws tags for all exceptions
+ *
+ * @throws PHP_Exception1
+ * @throws PHP_Exception2
+ */
+ public function okVariableRethrown()
+ {
+ throw new PHP_Exception1('Error');
+
+ try {
+ // Do something.
+ } catch (PHP_Exception2 $e) {
+ logError();
+ throw $e;
+ }
+
+ }//end okVariableRethrown()
+
+ /**
+ * Has correct throws tags for all exceptions
+ *
+ * @throws PHP_Exception1
+ * @throws PHP_Exception2
+ */
+ public function okVariableMultiRethrown()
+ {
+ try {
+ // Do something.
+ } catch (PHP_Exception1 | PHP_Exception2 $e) {
+ logError();
+ throw $e;
+ }
+
+ }//end okVariableMultiRethrown()
+}//end class
+
+class NamespacedException {
+ /**
+ * @throws \Exception
+ */
+ public function foo() {
+ throw new \Exception();
+ }
+
+ /**
+ * @throws \Foo\Bar\FooBarException
+ */
+ public function fooBar2() {
+ throw new \Foo\Bar\FooBarException();
+ }
+
+ /**
+ * @throws FooBarException
+ */
+ public function fooBar2() {
+ throw new \Foo\Bar\FooBarException();
+ }
+}
+
+class Foo {
+ /**
+ * Comment
+ */
+ public function foo() {
+ }//end foo()
+
+ public function bar() {
+ throw new Exception();
+ }
+
+ /**
+ * Returns information for a test.
+ *
+ * This info includes parameters, their valid values.
+ *
+ * @param integer $projectid Id of the project.
+ *
+ * @return array
+ * @throws ChannelException If the project is invalid.
+ */
+ public static function getTestInfo($projectid=NULL)
+ {
+ try {
+ DAL::beginTransaction();
+ DAL::commit();
+ } catch (DALException $e) {
+ DAL::rollBack();
+ throw new ChannelException($e);
+ }
+ }
+}
+
+class Test
+{
+ /**
+ * Folder name.
+ *
+ * @var string
+ */
+ protected $folderName;
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ if ( !strlen($this->folderName) ) {
+ throw new \RuntimeException('The $this->folderName must be specified before proceeding.');
+ }
+ }
+
+ /*
+ *
+ */
+ protected function foo()
+ {
+ }
+
+ /**
+ * @return Closure
+ */
+ public function getStuff()
+ {
+ return function () {
+ throw new RuntimeException("bam!");
+ };
+ }
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionCommentThrowTag 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\Standards\Squiz\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionCommentThrowTagUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 9 => 1,
+ 21 => 1,
+ 35 => 1,
+ 47 => 1,
+ 61 => 2,
+ 106 => 1,
+ 123 => 1,
+ 200 => 1,
+ 219 => 1,
+ 287 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class PHP_CodeSniffer_File
+{
+
+ /**
+ * a simple function comment.
+ *
+ * long desc here
+ *
+ * @param bool $stackPtr The position in @ @unknown the stack of the token
+ * that opened the scope
+ * @param int $detph How many scope levels down we are.
+ * @param string $index The index
+ * @return
+ * @throws
+ */
+ private function _functionCall($stackPtr, $depth=1, $index)
+ {
+ return $stackPtr;
+
+ }//end _functionCall()
+
+ //
+ // Sample function comment
+ //
+ //
+ //
+ public function invalidCommentStyle()
+ {
+
+ }//end invalidCommentStyle()
+
+
+ /**
+ *
+ * A simple function comment
+ * Span multiple line
+ *
+ *
+ * 0Long description with extra blank line before and after
+ *
+ *
+ * @return void
+ */
+ public function extraDescriptionNewlines()
+ {
+ return true;
+ }//end extraDescriptionNewlines()
+
+
+ /**
+ * -A simple function comment.
+ * @return void
+ */
+ public function missingNewlinesBeforeTags()
+ {
+ return;
+ }//end missingNewlinesBeforeTags()
+
+
+ /**
+ * Access tag should not be treated as a long description.
+ *
+ * @access public
+ * @return void
+ */
+ public function accessTag()
+ {
+
+ }//end accessTag()
+
+ /**
+ * Constructor.
+ *
+ * No return tag
+ */
+ function PHP_CodeSniffer_File()
+ {
+ return;
+ }
+
+
+ /**
+ * Destructor.
+ *
+ * No return tag too
+ */
+ function _PHP_CodeSniffer_File()
+ {
+ return;
+ }
+
+
+ /**
+ * Destructor PHP5.
+ */
+ function __destruct()
+ {
+ return;
+ }
+
+
+ function missingComment()
+ {
+ return;
+ }
+
+
+ /**
+ * no return tag.
+ *
+ */
+ public function noReturn($one)
+ {
+
+ }//end noReturn()
+
+
+ /**
+ * Param not immediate.
+ *
+ * @return
+ * @param int $threeSpaces
+ * @param integer $superfluous
+ * @param miss
+ * @param
+ */
+ public function missingDescription($threeSpaces)
+ {
+
+ }//end missingDescription()
+
+
+ /**
+ * Comments not capitalised.
+ *
+ * @param integer $one comment
+ * @param array $two -comment
+ * @param MyClas $three 0comment
+ *
+ * @return void
+ */
+ public function oneSpaceAfterLongestVar($one, $two, MyClass $three)
+ {
+
+ }//end oneSpaceAfterLongestVar()
+
+
+}//end class
+
+
+/**
+ * A simple function comment.
+ *
+ * @param string &$str The string passed in by reference
+ * @param string $foo The string passed in by reference.
+ *
+ * @return void
+ * @return
+ */
+public function functionOutsideClass(&$str, &$foo)
+{
+ return;
+}//end functionOutsideClass()
+
+function missingCommentOutsideClass()
+{
+ return;
+}//end missingCommentOutsideClass()
+
+
+?><?php
+function tagBeforeComment()
+{
+ return;
+}//end tagBeforeComment()
+
+
+/**
+ * no return tag.
+ *
+ * @see FunctionCommentSniff.php
+ */
+public function noReturnOutsideClass()
+{
+
+}//end noReturnOutsideClass()
+
+
+/**
+ * Missing param comment.
+ *
+ * @param integer $one comment
+ *
+ * @see wrong indent
+ * @see
+ * @return void
+ * @extra Invalid tag
+ * @throws UnknownException unknown
+ */
+public function missingTwoParamComment($one, $two, $three)
+{
+
+}//end missingTwoParamComment()
+
+
+/**
+ * Missing return type.
+ *
+ * @throws ExceptionWithNoComment
+ * @return
+ */
+public function missingReturnType()
+{
+
+}//end missingReturnType()
+
+
+/**
+ * Case-sensitive var type check with multiple return type.
+ *
+ * @param String $a1 Comment here.
+ * @param BoOL $a2 Comment here.
+ * @param INT $a3 Comment here.
+ * @param aRRay $a4 Comment here.
+ * @param real $a5 Comment here.
+ * @param double $a6 Comment here.
+ * @param Myclass $a7 Comment here.
+ *
+ * @return int|object|double|float|array(int=>MyClass)
+ */
+public function caseSensitive($a1, $a2, $a3, arRay $a4, $a5, $a6, myclas $a7)
+{
+
+}//end caseSensitive()
+
+
+/**
+ * More type hint check for custom type and array.
+ *
+ * @param array $a1 Comment here.
+ * @param array $a2 Comment here.
+ * @param MyClass $a3 Comment here.
+ * @param MyClass $a4 Comment here.
+ *
+ * @return array(int => MyClass)
+ */
+public function typeHint(MyClass $a1, $a2, myclass $a3, $a4)
+{
+ return (3 => 'myclass obj');
+
+}//end typeHint()
+
+
+/**
+ * Mixed variable type separated by a '|'.
+ *
+ * @param array|string $a1 Comment here.
+ * @param mixed $a2 Comment here.
+ * @param string|array $a3 Comment here.
+ * @param MyClass|int $a4 Comment here.
+ *
+ * @return bool
+ */
+public function mixedType($a1, $a2, $a3, $a4)
+{
+ return true;
+
+}//end mixedType()
+
+
+/**
+ * Array type.
+ *
+ * @param array(MyClass) $a1 OK.
+ * @param array() $a2 Invalid type.
+ * @param array( $a3 Typo.
+ * @param array(int) $a4 Use 'array(integer)' instead.
+ * @param array(int => integer) $a5 Use 'array(integer => integer)' instead.
+ * @param array(integer => bool) $a6 Use 'array(integer => boolean)' instead.
+ * @param aRRay $a7 Use 'array' instead.
+ * @param string $a8 String with unknown type hint.
+ *
+ * @return int
+ */
+public function mixedArrayType($a1, $a2, array $a3, $a4, $a5, $a6, $a7, unknownTypeHint $a8)
+{
+ return 1;
+
+}//end mixedArrayType()
+
+
+/**
+ */
+function empty1()
+{
+}//end empty1()
+
+
+/**
+ *
+ */
+function empty2()
+{
+}//end empty2()
+
+
+/**
+ *
+ *
+ *
+ */
+function empty3()
+{
+}//end empty3
+
+
+/**
+ * @return boolean
+ */
+public function missingShortDescriptionInFunctionComment()
+{
+ return true;
+
+}//end missingShortDescriptionInFunctionComment()
+
+
+class Another_Class
+{
+
+ /**
+ * Destructor should not include a return tag.
+ *
+ * @return void
+ */
+ function __destruct()
+ {
+ return;
+ }
+
+ /**
+ * Constructor should not include a return tag.
+ *
+ * @return void
+ */
+ function __construct()
+ {
+ return;
+ }
+
+}//end class
+
+
+/**
+ * Comment param alignment test.
+ *
+ * @param string $varrr1 Comment1..
+ * @param string $vr2 Comment2.
+ * @param string $var3 Comment3..
+ *
+ * @return void
+ */
+public static function paramAlign($varrr1, $vr2, $var3)
+{
+
+}//end paramAlign()
+
+
+/**
+ * Comment.
+ *
+ * @param string $id Comment.
+ * @param array $design Comment.
+ *
+ * @return void
+ */
+public static function paint($id, array $design)
+{
+
+}//end paint()
+
+
+/**
+ * Adds specified class name to class attribute of this widget.
+ *
+ * @since 4.0.0
+ * @return string
+ */
+public function myFunction()
+{
+ if ($condition === FALSE) {
+ echo 'hi';
+ }
+
+}//end myFunction()
+
+
+/**
+ * Adds specified class name to class attribute of this widget.
+ *
+ * @return string
+ */
+public function myFunction()
+{
+ if ($condition === FALSE) {
+ echo 'hi';
+ return;
+ }
+
+ return 'blah';
+
+}//end myFunction()
+
+
+/**
+ * Adds specified class name to class attribute of this widget.
+ *
+ * @return string
+ */
+public function myFunction()
+{
+ if ($condition === FALSE) {
+ echo 'hi';
+ }
+
+ return 'blah';
+
+}//end myFunction()
+
+/**
+ * Test function.
+ *
+ * @param string $arg1 An argument
+ *
+ * @access public
+ * @return bool
+ */
+
+echo $blah;
+
+function myFunction($arg1) {}
+
+class MyClass() {
+ /**
+ * An abstract function.
+ *
+ * @return string[]
+ */
+ abstract final protected function myFunction();
+}
+
+/**
+ * Comment.
+ *
+ * @param mixed $test An argument.
+ *
+ * @return mixed
+ */
+function test($test)
+{
+ if ($test === TRUE) {
+ return;
+ }
+
+ return $test;
+
+}//end test()
+
+
+/** Comment.
+ *
+ * @return mixed
+ *
+ */
+function test()
+{
+
+}//end test()
+
+/**
+ * Comment.
+ *
+ * @param \other\ns\item $test An argument.
+ *
+ * @return mixed
+ */
+function test(\other\ns\item $test)
+{
+ return $test;
+
+}//end test()
+
+/**
+ * Comment.
+ *
+ * @param item $test An argument.
+ *
+ * @return mixed
+ */
+function test(\other\ns\item $test)
+{
+ return $test;
+
+}//end test()
+
+/**
+ * Comment.
+ *
+ * @param \first\ns\item $test An argument.
+ *
+ * @return mixed
+ */
+function test(\first\ns\item $test = \second\ns::CONSTANT)
+{
+ return $test;
+
+}//end test()
+
+/**
+ * Comment.
+ *
+ * @param \first\item $test An argument.
+ *
+ * @return mixed
+ */
+function test(\first\ns\item $test = \second\ns::CONSTANT)
+{
+ return $test;
+
+}//end test()
+
+// Closures should be ignored.
+preg_replace_callback(
+ '~-([a-z])~',
+ function ($match) {
+ return strtoupper($match[1]);
+ },
+ 'hello-world'
+);
+
+$callback = function ($bar) use ($foo)
+ {
+ $bar += $foo;
+ };
+
+/**
+ * Comment should end with '*', not '**' before the slash.
+ **/
+function test123() {
+
+}
+
+/**
+ * Cant use resource for type hint.
+ *
+ * @param resource $test An argument.
+ *
+ * @return mixed
+ */
+function test($test)
+{
+ return $test;
+
+}//end test()
+
+/**
+ * Variable number of args.
+ *
+ * @param string $a1 Comment here.
+ * @param string $a2 Comment here.
+ * @param string $a2,... Comment here.
+ *
+ * @return boolean
+ */
+public function variableArgs($a1, $a2)
+{
+ return true;
+
+}//end variableArgs()
+
+/**
+ * Contains closure.
+ *
+ * @return void
+ */
+public function containsClosure()
+{
+ function ($e) {
+ return new Event($e);
+ },
+
+}//end containsClosure()
+
+/**
+ * 这是一条测试评论.
+ *
+ * @return void
+ */
+public function test()
+{
+
+}//end variableArgs()
+
+/**
+ * Uses callable.
+ *
+ * @param callable $cb Test parameter.
+ *
+ * @return void
+ */
+public function usesCallable(callable $cb) {
+ $cb();
+}//end usesCallable()
+
+/**
+ * Creates a map of tokens => line numbers for each token.
+ *
+ * @param array $tokens The array of tokens to process.
+ * @param object $tokenizer The tokenizer being used to process this file.
+ * @param string $eolChar The EOL character
+ * to use for splitting strings.
+ *
+ * @return void
+ * @throws Exception If something really bad
+ * happens while doing foo.
+ */
+public function foo(array &$tokens, $tokenizer, $eolChar)
+{
+
+}//end foo()
+
+/**
+ * Some description.
+ *
+ * @param \Vendor\Package\SomeClass $someclass Some class.
+ * @param \OtherVendor\Package\SomeClass2 $someclass2 Some class.
+ * @param \OtherVendor\Package\SomeClass2 $someclass3 Some class.
+ *
+ * @return void
+ */
+public function foo(SomeClass $someclass, \OtherVendor\Package\SomeClass2 $someclass2, SomeClass3 $someclass3)
+{
+}
+
+/**
+ * Gettext.
+ *
+ * @return string
+ */
+public function _() {
+ return $foo;
+}
+
+class Baz {
+ /**
+ * The PHP5 constructor
+ *
+ * No return tag
+ */
+ public function __construct() {
+
+ }
+}
+
+/**
+ * Test
+ *
+ * @return void
+ * @throws E
+ */
+function myFunction() {}
+
+/**
+ * Yield test
+ *
+ * @return integer
+ */
+function yieldTest()
+{
+ for ($i = 0; $i < 5; $i++) {
+ yield $i;
+ }
+}
+
+/**
+ * Yield test
+ *
+ * @return void
+ */
+function yieldTest()
+{
+ for ($i = 0; $i < 5; $i++) {
+ yield $i;
+ }
+}
+
+/**
+ * Using "array" as a type hint should satisfy a specified array parameter type.
+ *
+ * @param MyClass[] $values An array of MyClass objects.
+ *
+ * @return void
+ */
+public function specifiedArray(array $values) {
+
+}// end specifiedArray()
+
+/**
+ * Using "callable" as a type hint should satisfy a "callback" parameter type.
+ *
+ * @param callback $cb A callback.
+ *
+ * @return void
+ */
+public function callableCallback(callable $cb) {
+
+}// end callableCallback()
+
+/**
+ * PHP7 type hints.
+ *
+ * @param string $name1 Comment.
+ * @param integer $name2 Comment.
+ * @param float $name3 Comment.
+ * @param boolean $name4 Comment.
+ *
+ * @return void
+ */
+public function myFunction (string $name1, int $name2, float $name3, bool $name4) {
+}
+
+/**
+ * Variadic function.
+ *
+ * @param string $name1 Comment.
+ * @param string ...$name2 Comment.
+ *
+ * @return void
+ */
+public function myFunction(string $name1, string ...$name2) {
+}
+
+
+/**
+ * Variadic function.
+ *
+ * @param string $name1 Comment.
+ * @param string $name2 Comment.
+ *
+ * @return void
+ */
+public function myFunction(string $name1, string ...$name2) {
+}
+
+/**
+ * Return description function + correct type.
+ *
+ * @return integer This is a description.
+ */
+public function myFunction() {
+ return 5;
+}
+
+/**
+ * Return description function + incorrect type.
+ *
+ * @return int This is a description.
+ */
+public function myFunction() {
+ return 5;
+}
+
+/**
+ * Return description function + no return.
+ *
+ * @return void This is a description.
+ */
+public function myFunction() {
+}
+
+/**
+ * Return description function + mixed return.
+ *
+ * @return mixed This is a description.
+ */
+public function myFunction() {
+}
+
+/**
+ * Function comment.
+ *
+ * @param $bar
+ * Comment here.
+ * @param ...
+ * Additional arguments here.
+ *
+ * @return
+ * Return value
+ *
+ */
+function foo($bar) {
+}
+
+/**
+ * Do something.
+ *
+ * @return void
+ */
+public function someFunc(): void
+{
+ $class = new class
+ {
+ /**
+ * Do something.
+ *
+ * @return string
+ */
+ public function getString(): string
+ {
+ return 'some string';
+ }
+ };
+}
+
+/**
+ * Return description function + mixed return types.
+ *
+ * @return bool|int This is a description.
+ */
+function returnTypeWithDescriptionA()
+{
+ return 5;
+
+}//end returnTypeWithDescriptionA()
+
+
+/**
+ * Return description function + mixed return types.
+ *
+ * @return real|bool This is a description.
+ */
+function returnTypeWithDescriptionB()
+{
+ return 5;
+
+}//end returnTypeWithDescriptionB()
+
+
+/**
+ * Return description function + lots of different mixed return types.
+ *
+ * @return int|object|string[]|real|double|float|bool|array(int=>MyClass)|callable And here we have a description
+ */
+function returnTypeWithDescriptionC()
+{
+ return 5;
+
+}//end returnTypeWithDescriptionC()
+
+
+/**
+ * Return description function + lots of different mixed return types.
+ *
+ * @return array(int=>bool)|\OtherVendor\Package\SomeClass2|MyClass[]|void And here we have a description
+ */
+function returnTypeWithDescriptionD()
+{
+
+}//end returnTypeWithDescriptionD()
+
+/**
+ * Yield from test
+ *
+ * @return int[]
+ */
+function yieldFromTest()
+{
+ yield from foo();
+}
+
+/**
+ * Audio
+ *
+ * Generates an audio element to embed sounds
+ *
+ * @param mixed $src Either a source string or
+ * an array of sources.
+ * @param mixed $unsupportedMessage The message to display
+ * if the media tag is not supported by the browser.
+ * @param mixed $attributes HTML attributes.
+ * @return string
+ */
+function audio(
+ $src,
+ $unsupportedMessage = '',
+ $attributes = '',
+)
+{
+ return 'test';
+}
+
+/**
+ * Test function
+ *
+ * @return array
+ */
+function returnArrayWithClosure()
+{
+ function () {
+ return;
+ };
+
+ return [];
+}
+
+/**
+ * Test function
+ *
+ * @return array
+ */
+function returnArrayWithAnonymousClass()
+{
+ new class {
+ /**
+ * @return void
+ */
+ public function test() {
+ return;
+ }
+ };
+
+ return [];
+}
+
+/**
+ * @return void
+ */
+function returnVoidWithClosure()
+{
+ function () {
+ return 1;
+ };
+}
+
+/**
+ * @return void
+ */
+function returnVoidWithAnonymousClass()
+{
+ new class {
+ /**
+ * @return integer
+ */
+ public function test()
+ {
+ return 1;
+ }
+ };
+}
+
+class TestReturnVoid
+{
+ /**
+ * @return void
+ */
+ public function test()
+ {
+ function () {
+ return 4;
+ };
+ }
+}
--- /dev/null
+<?php
+class PHP_CodeSniffer_File
+{
+
+ /**
+ * a simple function comment.
+ *
+ * long desc here
+ *
+ * @param boolean $stackPtr The position in @ @unknown the stack of the token
+ * that opened the scope
+ * @param integer $detph How many scope levels down we are.
+ * @param string $index The index
+ * @return
+ * @throws
+ */
+ private function _functionCall($stackPtr, $depth=1, $index)
+ {
+ return $stackPtr;
+
+ }//end _functionCall()
+
+ //
+ // Sample function comment
+ //
+ //
+ //
+ public function invalidCommentStyle()
+ {
+
+ }//end invalidCommentStyle()
+
+
+ /**
+ *
+ * A simple function comment
+ * Span multiple line
+ *
+ *
+ * 0Long description with extra blank line before and after
+ *
+ *
+ * @return void
+ */
+ public function extraDescriptionNewlines()
+ {
+ return true;
+ }//end extraDescriptionNewlines()
+
+
+ /**
+ * -A simple function comment.
+ * @return void
+ */
+ public function missingNewlinesBeforeTags()
+ {
+ return;
+ }//end missingNewlinesBeforeTags()
+
+
+ /**
+ * Access tag should not be treated as a long description.
+ *
+ * @access public
+ * @return void
+ */
+ public function accessTag()
+ {
+
+ }//end accessTag()
+
+ /**
+ * Constructor.
+ *
+ * No return tag
+ */
+ function PHP_CodeSniffer_File()
+ {
+ return;
+ }
+
+
+ /**
+ * Destructor.
+ *
+ * No return tag too
+ */
+ function _PHP_CodeSniffer_File()
+ {
+ return;
+ }
+
+
+ /**
+ * Destructor PHP5.
+ */
+ function __destruct()
+ {
+ return;
+ }
+
+
+ function missingComment()
+ {
+ return;
+ }
+
+
+ /**
+ * no return tag.
+ *
+ */
+ public function noReturn($one)
+ {
+
+ }//end noReturn()
+
+
+ /**
+ * Param not immediate.
+ *
+ * @return
+ * @param integer $threeSpaces
+ * @param integer $superfluous
+ * @param miss
+ * @param
+ */
+ public function missingDescription($threeSpaces)
+ {
+
+ }//end missingDescription()
+
+
+ /**
+ * Comments not capitalised.
+ *
+ * @param integer $one comment
+ * @param array $two -comment
+ * @param MyClas $three 0comment
+ *
+ * @return void
+ */
+ public function oneSpaceAfterLongestVar($one, $two, MyClass $three)
+ {
+
+ }//end oneSpaceAfterLongestVar()
+
+
+}//end class
+
+
+/**
+ * A simple function comment.
+ *
+ * @param string &$str The string passed in by reference
+ * @param string $foo The string passed in by reference.
+ *
+ * @return void
+ * @return
+ */
+public function functionOutsideClass(&$str, &$foo)
+{
+ return;
+}//end functionOutsideClass()
+
+function missingCommentOutsideClass()
+{
+ return;
+}//end missingCommentOutsideClass()
+
+
+?><?php
+function tagBeforeComment()
+{
+ return;
+}//end tagBeforeComment()
+
+
+/**
+ * no return tag.
+ *
+ * @see FunctionCommentSniff.php
+ */
+public function noReturnOutsideClass()
+{
+
+}//end noReturnOutsideClass()
+
+
+/**
+ * Missing param comment.
+ *
+ * @param integer $one comment
+ *
+ * @see wrong indent
+ * @see
+ * @return void
+ * @extra Invalid tag
+ * @throws UnknownException unknown
+ */
+public function missingTwoParamComment($one, $two, $three)
+{
+
+}//end missingTwoParamComment()
+
+
+/**
+ * Missing return type.
+ *
+ * @throws ExceptionWithNoComment
+ * @return
+ */
+public function missingReturnType()
+{
+
+}//end missingReturnType()
+
+
+/**
+ * Case-sensitive var type check with multiple return type.
+ *
+ * @param string $a1 Comment here.
+ * @param boolean $a2 Comment here.
+ * @param integer $a3 Comment here.
+ * @param array $a4 Comment here.
+ * @param float $a5 Comment here.
+ * @param float $a6 Comment here.
+ * @param Myclass $a7 Comment here.
+ *
+ * @return integer|object|float|array(integer => MyClass)
+ */
+public function caseSensitive($a1, $a2, $a3, arRay $a4, $a5, $a6, myclas $a7)
+{
+
+}//end caseSensitive()
+
+
+/**
+ * More type hint check for custom type and array.
+ *
+ * @param array $a1 Comment here.
+ * @param array $a2 Comment here.
+ * @param MyClass $a3 Comment here.
+ * @param MyClass $a4 Comment here.
+ *
+ * @return array(integer => MyClass)
+ */
+public function typeHint(MyClass $a1, $a2, myclass $a3, $a4)
+{
+ return (3 => 'myclass obj');
+
+}//end typeHint()
+
+
+/**
+ * Mixed variable type separated by a '|'.
+ *
+ * @param array|string $a1 Comment here.
+ * @param mixed $a2 Comment here.
+ * @param string|array $a3 Comment here.
+ * @param MyClass|integer $a4 Comment here.
+ *
+ * @return boolean
+ */
+public function mixedType($a1, $a2, $a3, $a4)
+{
+ return true;
+
+}//end mixedType()
+
+
+/**
+ * Array type.
+ *
+ * @param array(MyClass) $a1 OK.
+ * @param array $a2 Invalid type.
+ * @param array $a3 Typo.
+ * @param array(integer) $a4 Use 'array(integer)' instead.
+ * @param array(integer => integer) $a5 Use 'array(integer => integer)' instead.
+ * @param array(integer => boolean) $a6 Use 'array(integer => boolean)' instead.
+ * @param array $a7 Use 'array' instead.
+ * @param string $a8 String with unknown type hint.
+ *
+ * @return integer
+ */
+public function mixedArrayType($a1, $a2, array $a3, $a4, $a5, $a6, $a7, unknownTypeHint $a8)
+{
+ return 1;
+
+}//end mixedArrayType()
+
+
+/**
+ */
+function empty1()
+{
+}//end empty1()
+
+
+/**
+ *
+ */
+function empty2()
+{
+}//end empty2()
+
+
+/**
+ *
+ *
+ *
+ */
+function empty3()
+{
+}//end empty3
+
+
+/**
+ * @return boolean
+ */
+public function missingShortDescriptionInFunctionComment()
+{
+ return true;
+
+}//end missingShortDescriptionInFunctionComment()
+
+
+class Another_Class
+{
+
+ /**
+ * Destructor should not include a return tag.
+ *
+ * @return void
+ */
+ function __destruct()
+ {
+ return;
+ }
+
+ /**
+ * Constructor should not include a return tag.
+ *
+ * @return void
+ */
+ function __construct()
+ {
+ return;
+ }
+
+}//end class
+
+
+/**
+ * Comment param alignment test.
+ *
+ * @param string $varrr1 Comment1..
+ * @param string $vr2 Comment2.
+ * @param string $var3 Comment3..
+ *
+ * @return void
+ */
+public static function paramAlign($varrr1, $vr2, $var3)
+{
+
+}//end paramAlign()
+
+
+/**
+ * Comment.
+ *
+ * @param string $id Comment.
+ * @param array $design Comment.
+ *
+ * @return void
+ */
+public static function paint($id, array $design)
+{
+
+}//end paint()
+
+
+/**
+ * Adds specified class name to class attribute of this widget.
+ *
+ * @since 4.0.0
+ * @return string
+ */
+public function myFunction()
+{
+ if ($condition === FALSE) {
+ echo 'hi';
+ }
+
+}//end myFunction()
+
+
+/**
+ * Adds specified class name to class attribute of this widget.
+ *
+ * @return string
+ */
+public function myFunction()
+{
+ if ($condition === FALSE) {
+ echo 'hi';
+ return;
+ }
+
+ return 'blah';
+
+}//end myFunction()
+
+
+/**
+ * Adds specified class name to class attribute of this widget.
+ *
+ * @return string
+ */
+public function myFunction()
+{
+ if ($condition === FALSE) {
+ echo 'hi';
+ }
+
+ return 'blah';
+
+}//end myFunction()
+
+/**
+ * Test function.
+ *
+ * @param string $arg1 An argument
+ *
+ * @access public
+ * @return bool
+ */
+
+echo $blah;
+
+function myFunction($arg1) {}
+
+class MyClass() {
+ /**
+ * An abstract function.
+ *
+ * @return string[]
+ */
+ abstract final protected function myFunction();
+}
+
+/**
+ * Comment.
+ *
+ * @param mixed $test An argument.
+ *
+ * @return mixed
+ */
+function test($test)
+{
+ if ($test === TRUE) {
+ return;
+ }
+
+ return $test;
+
+}//end test()
+
+
+/** Comment.
+ *
+ * @return mixed
+ *
+ */
+function test()
+{
+
+}//end test()
+
+/**
+ * Comment.
+ *
+ * @param \other\ns\item $test An argument.
+ *
+ * @return mixed
+ */
+function test(\other\ns\item $test)
+{
+ return $test;
+
+}//end test()
+
+/**
+ * Comment.
+ *
+ * @param item $test An argument.
+ *
+ * @return mixed
+ */
+function test(\other\ns\item $test)
+{
+ return $test;
+
+}//end test()
+
+/**
+ * Comment.
+ *
+ * @param \first\ns\item $test An argument.
+ *
+ * @return mixed
+ */
+function test(\first\ns\item $test = \second\ns::CONSTANT)
+{
+ return $test;
+
+}//end test()
+
+/**
+ * Comment.
+ *
+ * @param \first\item $test An argument.
+ *
+ * @return mixed
+ */
+function test(\first\ns\item $test = \second\ns::CONSTANT)
+{
+ return $test;
+
+}//end test()
+
+// Closures should be ignored.
+preg_replace_callback(
+ '~-([a-z])~',
+ function ($match) {
+ return strtoupper($match[1]);
+ },
+ 'hello-world'
+);
+
+$callback = function ($bar) use ($foo)
+ {
+ $bar += $foo;
+ };
+
+/**
+ * Comment should end with '*', not '**' before the slash.
+ **/
+function test123() {
+
+}
+
+/**
+ * Cant use resource for type hint.
+ *
+ * @param resource $test An argument.
+ *
+ * @return mixed
+ */
+function test($test)
+{
+ return $test;
+
+}//end test()
+
+/**
+ * Variable number of args.
+ *
+ * @param string $a1 Comment here.
+ * @param string $a2 Comment here.
+ * @param string $a2,... Comment here.
+ *
+ * @return boolean
+ */
+public function variableArgs($a1, $a2)
+{
+ return true;
+
+}//end variableArgs()
+
+/**
+ * Contains closure.
+ *
+ * @return void
+ */
+public function containsClosure()
+{
+ function ($e) {
+ return new Event($e);
+ },
+
+}//end containsClosure()
+
+/**
+ * 这是一条测试评论.
+ *
+ * @return void
+ */
+public function test()
+{
+
+}//end variableArgs()
+
+/**
+ * Uses callable.
+ *
+ * @param callable $cb Test parameter.
+ *
+ * @return void
+ */
+public function usesCallable(callable $cb) {
+ $cb();
+}//end usesCallable()
+
+/**
+ * Creates a map of tokens => line numbers for each token.
+ *
+ * @param array $tokens The array of tokens to process.
+ * @param object $tokenizer The tokenizer being used to process this file.
+ * @param string $eolChar The EOL character
+ * to use for splitting strings.
+ *
+ * @return void
+ * @throws Exception If something really bad
+ * happens while doing foo.
+ */
+public function foo(array &$tokens, $tokenizer, $eolChar)
+{
+
+}//end foo()
+
+/**
+ * Some description.
+ *
+ * @param \Vendor\Package\SomeClass $someclass Some class.
+ * @param \OtherVendor\Package\SomeClass2 $someclass2 Some class.
+ * @param \OtherVendor\Package\SomeClass2 $someclass3 Some class.
+ *
+ * @return void
+ */
+public function foo(SomeClass $someclass, \OtherVendor\Package\SomeClass2 $someclass2, SomeClass3 $someclass3)
+{
+}
+
+/**
+ * Gettext.
+ *
+ * @return string
+ */
+public function _() {
+ return $foo;
+}
+
+class Baz {
+ /**
+ * The PHP5 constructor
+ *
+ * No return tag
+ */
+ public function __construct() {
+
+ }
+}
+
+/**
+ * Test
+ *
+ * @return void
+ * @throws E
+ */
+function myFunction() {}
+
+/**
+ * Yield test
+ *
+ * @return integer
+ */
+function yieldTest()
+{
+ for ($i = 0; $i < 5; $i++) {
+ yield $i;
+ }
+}
+
+/**
+ * Yield test
+ *
+ * @return void
+ */
+function yieldTest()
+{
+ for ($i = 0; $i < 5; $i++) {
+ yield $i;
+ }
+}
+
+/**
+ * Using "array" as a type hint should satisfy a specified array parameter type.
+ *
+ * @param MyClass[] $values An array of MyClass objects.
+ *
+ * @return void
+ */
+public function specifiedArray(array $values) {
+
+}// end specifiedArray()
+
+/**
+ * Using "callable" as a type hint should satisfy a "callback" parameter type.
+ *
+ * @param callback $cb A callback.
+ *
+ * @return void
+ */
+public function callableCallback(callable $cb) {
+
+}// end callableCallback()
+
+/**
+ * PHP7 type hints.
+ *
+ * @param string $name1 Comment.
+ * @param integer $name2 Comment.
+ * @param float $name3 Comment.
+ * @param boolean $name4 Comment.
+ *
+ * @return void
+ */
+public function myFunction (string $name1, int $name2, float $name3, bool $name4) {
+}
+
+/**
+ * Variadic function.
+ *
+ * @param string $name1 Comment.
+ * @param string ...$name2 Comment.
+ *
+ * @return void
+ */
+public function myFunction(string $name1, string ...$name2) {
+}
+
+
+/**
+ * Variadic function.
+ *
+ * @param string $name1 Comment.
+ * @param string $name2 Comment.
+ *
+ * @return void
+ */
+public function myFunction(string $name1, string ...$name2) {
+}
+
+/**
+ * Return description function + correct type.
+ *
+ * @return integer This is a description.
+ */
+public function myFunction() {
+ return 5;
+}
+
+/**
+ * Return description function + incorrect type.
+ *
+ * @return integer This is a description.
+ */
+public function myFunction() {
+ return 5;
+}
+
+/**
+ * Return description function + no return.
+ *
+ * @return void This is a description.
+ */
+public function myFunction() {
+}
+
+/**
+ * Return description function + mixed return.
+ *
+ * @return mixed This is a description.
+ */
+public function myFunction() {
+}
+
+/**
+ * Function comment.
+ *
+ * @param $bar
+ * Comment here.
+ * @param ...
+ * Additional arguments here.
+ *
+ * @return
+ * Return value
+ *
+ */
+function foo($bar) {
+}
+
+/**
+ * Do something.
+ *
+ * @return void
+ */
+public function someFunc(): void
+{
+ $class = new class
+ {
+ /**
+ * Do something.
+ *
+ * @return string
+ */
+ public function getString(): string
+ {
+ return 'some string';
+ }
+ };
+}
+
+/**
+ * Return description function + mixed return types.
+ *
+ * @return boolean|integer This is a description.
+ */
+function returnTypeWithDescriptionA()
+{
+ return 5;
+
+}//end returnTypeWithDescriptionA()
+
+
+/**
+ * Return description function + mixed return types.
+ *
+ * @return float|boolean This is a description.
+ */
+function returnTypeWithDescriptionB()
+{
+ return 5;
+
+}//end returnTypeWithDescriptionB()
+
+
+/**
+ * Return description function + lots of different mixed return types.
+ *
+ * @return integer|object|string[]|float|boolean|array(integer => MyClass)|callable And here we have a description
+ */
+function returnTypeWithDescriptionC()
+{
+ return 5;
+
+}//end returnTypeWithDescriptionC()
+
+
+/**
+ * Return description function + lots of different mixed return types.
+ *
+ * @return array(integer => boolean)|\OtherVendor\Package\SomeClass2|MyClass[]|void And here we have a description
+ */
+function returnTypeWithDescriptionD()
+{
+
+}//end returnTypeWithDescriptionD()
+
+/**
+ * Yield from test
+ *
+ * @return int[]
+ */
+function yieldFromTest()
+{
+ yield from foo();
+}
+
+/**
+ * Audio
+ *
+ * Generates an audio element to embed sounds
+ *
+ * @param mixed $src Either a source string or
+ * an array of sources.
+ * @param mixed $unsupportedMessage The message to display
+ * if the media tag is not supported by the browser.
+ * @param mixed $attributes HTML attributes.
+ * @return string
+ */
+function audio(
+ $src,
+ $unsupportedMessage = '',
+ $attributes = '',
+)
+{
+ return 'test';
+}
+
+/**
+ * Test function
+ *
+ * @return array
+ */
+function returnArrayWithClosure()
+{
+ function () {
+ return;
+ };
+
+ return [];
+}
+
+/**
+ * Test function
+ *
+ * @return array
+ */
+function returnArrayWithAnonymousClass()
+{
+ new class {
+ /**
+ * @return void
+ */
+ public function test() {
+ return;
+ }
+ };
+
+ return [];
+}
+
+/**
+ * @return void
+ */
+function returnVoidWithClosure()
+{
+ function () {
+ return 1;
+ };
+}
+
+/**
+ * @return void
+ */
+function returnVoidWithAnonymousClass()
+{
+ new class {
+ /**
+ * @return integer
+ */
+ public function test()
+ {
+ return 1;
+ }
+ };
+}
+
+class TestReturnVoid
+{
+ /**
+ * @return void
+ */
+ public function test()
+ {
+ function () {
+ return 4;
+ };
+ }
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionComment 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\Standards\Squiz\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ $errors = array(
+ 5 => 1,
+ 10 => 3,
+ 12 => 2,
+ 13 => 2,
+ 14 => 1,
+ 15 => 1,
+ 28 => 1,
+ 43 => 1,
+ 76 => 1,
+ 87 => 1,
+ 103 => 1,
+ 109 => 1,
+ 112 => 1,
+ 122 => 1,
+ 123 => 3,
+ 124 => 2,
+ 125 => 1,
+ 126 => 1,
+ 137 => 4,
+ 138 => 4,
+ 139 => 4,
+ 143 => 2,
+ 152 => 1,
+ 155 => 2,
+ 159 => 1,
+ 166 => 1,
+ 173 => 1,
+ 183 => 1,
+ 190 => 2,
+ 193 => 2,
+ 196 => 1,
+ 199 => 2,
+ 210 => 1,
+ 211 => 1,
+ 222 => 1,
+ 223 => 1,
+ 224 => 1,
+ 225 => 1,
+ 226 => 1,
+ 227 => 1,
+ 230 => 2,
+ 232 => 2,
+ 246 => 1,
+ 248 => 4,
+ 261 => 1,
+ 263 => 1,
+ 276 => 1,
+ 277 => 1,
+ 278 => 1,
+ 279 => 1,
+ 280 => 1,
+ 281 => 1,
+ 284 => 1,
+ 286 => 7,
+ 294 => 1,
+ 302 => 1,
+ 312 => 1,
+ 358 => 1,
+ 359 => 2,
+ 372 => 1,
+ 373 => 1,
+ 387 => 1,
+ 407 => 1,
+ 441 => 1,
+ 500 => 1,
+ 526 => 1,
+ 548 => 1,
+ 641 => 1,
+ 669 => 1,
+ 688 => 1,
+ 744 => 1,
+ 748 => 1,
+ 767 => 1,
+ 789 => 1,
+ 792 => 1,
+ 794 => 1,
+ 797 => 1,
+ 801 => 1,
+ 828 => 1,
+ 840 => 1,
+ 852 => 1,
+ 864 => 1,
+ 886 => 1,
+ 888 => 1,
+ 890 => 1,
+ );
+
+ // Scalar type hints only work from PHP 7 onwards.
+ if (PHP_VERSION_ID >= 70000) {
+ $errors[17] = 3;
+ $errors[128] = 1;
+ $errors[143] = 3;
+ $errors[161] = 2;
+ $errors[201] = 1;
+ $errors[232] = 7;
+ $errors[363] = 3;
+ $errors[377] = 1;
+ $errors[575] = 2;
+ $errors[627] = 1;
+ } else {
+ $errors[729] = 4;
+ $errors[740] = 2;
+ $errors[752] = 2;
+ }
+
+ return $errors;
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+// Some code goes here.
+$code = 'hello';
+
+// This comment contains # multiple
+// hash signs (#).
+$code = 'hello';
+
+/*
+ * Here is a small function comment.
+ */
+function test()
+{
+ // Some code goes here.
+ $code = 'hello';
+
+ # This comment is banned.
+ $code = 'hello';
+
+}//end test()
+
+/*
+ A longer comment goes here.
+ It spans multiple lines.
+*/
+
+# This is a long comment
+# that is banned.
+
+
+
+// some code goes here!
+
+// This comment contains # multiple
+// hash signs (#) but no
+// full stop
+$code = 'hello';
+
+/*
+ * Here is a small function comment
+ */
+function test()
+{
+ // Some code goes here
+
+}//end test()
+
+/*
+ A longer comment goes here.
+ It spans multiple lines!!
+ Or does it?
+*/
+
+// 0This is a simple multi-line
+// comment!
+$code = 'hello';
+
+//This is not valid.
+$code = 'hello';
+
+// Neither is this!
+$code = 'hello';
+
+//
+$code = 'hello';
+
+/** Neither is this! **/
+$code = 'hello';
+
+class MyClass
+{
+ /**
+ * Represents a left orientation for the widget.
+ *
+ * @var integer
+ * @since 4.0.0
+ */
+ const LEFT = 1;
+}
+
+/**
+ * Comment should be ignored.
+ *
+ */
+final class MyClass
+{
+ /**
+ * Comment should be ignored.
+ *
+ */
+ final public function test() {}
+}
+
+// 这是一条测试评论
+// -> One
+// -> One.One
+// -> Two
+
+/*
+ Here is some inline example code:
+ -> One
+ -> One.One
+ -> Two
+*/
+
+/**
+ * Comment should be ignored in PHP 5.4.
+ *
+ */
+trait MyTrait {
+
+}
+
+$foo = 'foo'; // Var set to foo.
+
+echo $foo;
+
+// Comment here.
+echo $foo;
+
+/**
+ * Comments about the include
+ */
+include_once($blah);
+
+// some comment without capital or full stop
+echo $foo; // An unrelated comment.
+
+// An unrelated comment.
+echo $foo; // some comment without capital or full stop
+
+/*
+ * N.B.: The below test line must be the last test in the file.
+ * Testing that a new line after an inline comment when it's the last non-whitespace
+ * token in a file, does *not* throw an error as this would conflict with the common
+ * "new line required at end of file" rule.
+ */
+
+// For this test line having an empty line below it, is fine.
--- /dev/null
+<?php
+// Some code goes here.
+$code = 'hello';
+
+// This comment contains # multiple
+// hash signs (#).
+$code = 'hello';
+
+/*
+ * Here is a small function comment.
+ */
+function test()
+{
+ // Some code goes here.
+ $code = 'hello';
+
+ // This comment is banned.
+ $code = 'hello';
+
+}//end test()
+
+/*
+ A longer comment goes here.
+ It spans multiple lines.
+*/
+
+// This is a long comment
+// that is banned.
+// some code goes here!
+// This comment contains # multiple
+// hash signs (#) but no
+// full stop
+$code = 'hello';
+
+/*
+ * Here is a small function comment
+ */
+function test()
+{
+ // Some code goes here
+}//end test()
+
+/*
+ A longer comment goes here.
+ It spans multiple lines!!
+ Or does it?
+*/
+
+// 0This is a simple multi-line
+// comment!
+$code = 'hello';
+
+// This is not valid.
+$code = 'hello';
+
+// Neither is this!
+$code = 'hello';
+
+$code = 'hello';
+
+/** Neither is this! **/
+$code = 'hello';
+
+class MyClass
+{
+ /**
+ * Represents a left orientation for the widget.
+ *
+ * @var integer
+ * @since 4.0.0
+ */
+ const LEFT = 1;
+}
+
+/**
+ * Comment should be ignored.
+ *
+ */
+final class MyClass
+{
+ /**
+ * Comment should be ignored.
+ *
+ */
+ final public function test() {}
+}
+
+// 这是一条测试评论
+// -> One
+// -> One.One
+// -> Two
+/*
+ Here is some inline example code:
+ -> One
+ -> One.One
+ -> Two
+*/
+
+/**
+ * Comment should be ignored in PHP 5.4.
+ *
+ */
+trait MyTrait {
+
+}
+
+$foo = 'foo'; // Var set to foo.
+
+echo $foo;
+
+// Comment here.
+echo $foo;
+
+/**
+ * Comments about the include
+ */
+include_once($blah);
+
+// some comment without capital or full stop
+echo $foo; // An unrelated comment.
+
+// An unrelated comment.
+echo $foo; // some comment without capital or full stop
+
+/*
+ * N.B.: The below test line must be the last test in the file.
+ * Testing that a new line after an inline comment when it's the last non-whitespace
+ * token in a file, does *not* throw an error as this would conflict with the common
+ * "new line required at end of file" rule.
+ */
+
+// For this test line having an empty line below it, is fine.
--- /dev/null
+// Some content here.
+var code = 'hello';
+
+// This comment contains # multiple
+// hash signs (#).
+code = 'hello';
+
+/**
+ * This is the first line of a function comment.
+ * This is the second line.
+ */
+function testFunction()
+{
+ // Callback methods which are added by external objects.
+ this.callbacks = {};
+
+}//end testFunction()
+
+/**
+ * This is the first line of a class comment.
+ * This is the second line.
+ */
+myClass.prototype = {
+
+ /**
+ * This is the first line of a method comment.
+ * This is the second line.
+ */
+ load: function(url, callback)
+ {
+ // Some code here.
+
+ }
+};
+
+// some code goes here!
+
+/*
+ A longer comment goes here.
+ It spans multiple lines!!
+ Or does it?
+*/
+
+// 0This is a simple multi-line
+// comment!
+code = 'hello';
+
+//This is not valid.
+code = 'hello';
+
+// Neither is this!
+code = 'hello';
+
+//
+code = 'hello';
+
+/** Neither is this! **/
+code = 'hello';
+
+/**
+ * This is the first line of a function comment.
+ * This is the second line.
+ */
+var myFunction = function() {
+}
+
+/**
+ * This is the first line of a function comment.
+ * This is the second line.
+ */
+myFunction = function() {
+}
+
+/**
+ * This is the first line of a function comment.
+ * This is the second line.
+ */
+myClass.myFunction = function() {
+}
+
+dfx.getIframeDocument = function(iframe)
+{
+ return doc;
+
+};//end dfx.getIframeDocument()
+
+mig.Gallery.prototype = {
+
+ init: function(cb)
+ {
+
+ },//end init()
+
+ imageClicked: function(id)
+ {
+
+ }//end imageClicked()
+
+};
+
+// Here is some inline example code:
+// -> One
+// -> One.One
+// -> Two
+
+/*
+ Here is some inline example code:
+ -> One
+ -> One.One
+ -> Two
+*/
+
+
+var foo = 'foo'; // Var set to foo.
+
+console.info(foo);
+
+// Comment here.
+console.info(foo);
+
+//**
+* invalid comment
+*/
+
+// some comment without capital or full stop
+console.log(foo); // An unrelated comment.
+
+// An unrelated comment.
+console.log(foo); // some comment without capital or full stop
--- /dev/null
+// Some content here.
+var code = 'hello';
+
+// This comment contains # multiple
+// hash signs (#).
+code = 'hello';
+
+/**
+ * This is the first line of a function comment.
+ * This is the second line.
+ */
+function testFunction()
+{
+ // Callback methods which are added by external objects.
+ this.callbacks = {};
+
+}//end testFunction()
+
+/**
+ * This is the first line of a class comment.
+ * This is the second line.
+ */
+myClass.prototype = {
+
+ /**
+ * This is the first line of a method comment.
+ * This is the second line.
+ */
+ load: function(url, callback)
+ {
+ // Some code here.
+ }
+};
+
+// some code goes here!
+/*
+ A longer comment goes here.
+ It spans multiple lines!!
+ Or does it?
+*/
+
+// 0This is a simple multi-line
+// comment!
+code = 'hello';
+
+// This is not valid.
+code = 'hello';
+
+// Neither is this!
+code = 'hello';
+
+code = 'hello';
+
+/** Neither is this! **/
+code = 'hello';
+
+/**
+ * This is the first line of a function comment.
+ * This is the second line.
+ */
+var myFunction = function() {
+}
+
+/**
+ * This is the first line of a function comment.
+ * This is the second line.
+ */
+myFunction = function() {
+}
+
+/**
+ * This is the first line of a function comment.
+ * This is the second line.
+ */
+myClass.myFunction = function() {
+}
+
+dfx.getIframeDocument = function(iframe)
+{
+ return doc;
+
+};//end dfx.getIframeDocument()
+
+mig.Gallery.prototype = {
+
+ init: function(cb)
+ {
+
+ },//end init()
+
+ imageClicked: function(id)
+ {
+
+ }//end imageClicked()
+
+};
+
+// Here is some inline example code:
+// -> One
+// -> One.One
+// -> Two
+/*
+ Here is some inline example code:
+ -> One
+ -> One.One
+ -> Two
+*/
+
+
+var foo = 'foo'; // Var set to foo.
+
+console.info(foo);
+
+// Comment here.
+console.info(foo);
+
+// **
+* invalid comment
+*/
+
+// some comment without capital or full stop
+console.log(foo); // An unrelated comment.
+
+// An unrelated comment.
+console.log(foo); // some comment without capital or full stop
--- /dev/null
+<?php
+/**
+ * Unit test class for the InlineComment 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\Standards\Squiz\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class InlineCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='InlineCommentUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'InlineCommentUnitTest.inc':
+ $errors = array(
+ 17 => 1,
+ 27 => 1,
+ 28 => 1,
+ 32 => 2,
+ 36 => 1,
+ 44 => 2,
+ 58 => 1,
+ 61 => 1,
+ 64 => 1,
+ 67 => 1,
+ 95 => 1,
+ 96 => 1,
+ 97 => 3,
+ 118 => 1,
+ 126 => 2,
+ 130 => 2,
+ );
+
+ return $errors;
+ case 'InlineCommentUnitTest.js':
+ return array(
+ 31 => 1,
+ 36 => 2,
+ 48 => 1,
+ 51 => 1,
+ 54 => 1,
+ 57 => 1,
+ 102 => 1,
+ 103 => 1,
+ 104 => 3,
+ 118 => 1,
+ 121 => 1,
+ 125 => 2,
+ 129 => 2,
+ );
+ default:
+ return array();
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+function long_function()
+{
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+ }//end if
+
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+ }
+
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+ } else {
+ // Short ELSE
+ }//end if
+
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+ } else {
+ // Short ELSE
+ }
+}
+
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end foreach
+
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ foreach ($blah as $val) {
+ // Short foreach.
+ }
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}
+
+for ($var =1; $var < 20; $var++) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end for
+
+for ($var =1; $var < 20; $var++) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ for ($val =1; $val < 20; $val++) {
+ // Short for.
+ }
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}
+
+while ($var < 20) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end while looping
+
+while ($var < 20) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ while ($something) {
+ // Short while.
+ }
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}
+
+if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+} //end if
+
+if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+} else {
+ // Short ELSE
+} //end if
+
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} //end foreach
+
+for ($var =1; $var < 20; $var++) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} //end for
+
+while ($var < 20) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} //end while
+
+while ($var < 20) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end for
+
+if (true) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} else if ($condition) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} elseif ($cond) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} else {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end if
+
+if ($something) {
+ // Line 1
+ // Line 2
+} else if ($somethingElse) {
+ // Line 1
+ // Line 2
+} else {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}
+
+switch ($something) {
+ case '1':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+ case '2':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+ case '3':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+ case '4':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+ case '5':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+}
+
+// Wrong comment
+if ($condition) {
+ echo "true";
+}//end foreach
+
+if ($condition) {
+ echo "true";
+} //end if
+
+try {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (Exception $e) {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+}
+
+try {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (Exception $e) {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+}
+
+try {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (Exception $e) {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+}//end catch
+
+try {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (DALException $e) {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (ChannelException $e) {
+ // some code here.
+} catch (Exception $e) {
+ // some code here.
+}
+
+switch ($foo) {
+ case 'one' : {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ break;
+ }
+ case 'one' :
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ break;
+}//end switch
+
+// Yes, code like this does exist.
+if ($foo) {
+ return $foo;
+} elseif ($bar)
+ return $bar;
+
+switch ($foo) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ case 40:
+ case 41:
+ case 42:
+ case 43:
+ case 44:
+ case 45:
+ case 46:
+ case 47:
+ case 48:
+ case 49:
+ case 50:
+ case 51:
+ case 52:
+ case 53:
+ case 54:
+ case 55:
+ case 56:
+ case 57:
+ case 58:
+ case 59:
+ echo $foo;
+ break;
+}
+
+// @codingStandardsChangeSetting Squiz.Commenting.LongConditionClosingComment lineLimit 5
+// @codingStandardsChangeSetting Squiz.Commenting.LongConditionClosingComment commentFormat //Dragonbait-%s
+
+function quite_long_function()
+{
+ // Ok - below limit
+ if ($longFunction) {
+ $variable = 'hello';
+ }
+
+ // Ok - correct comment
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ }//Dragonbait-if
+
+ // This should be caught - wrong comment
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+ } else {
+ // Short ELSE
+ }//end if
+
+ // This should be caught - no comment
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+ } else {
+ // Short ELSE
+ }
+}
+
+// @codingStandardsChangeSetting Squiz.Commenting.LongConditionClosingComment lineLimit 30
+// @codingStandardsChangeSetting Squiz.Commenting.LongConditionClosingComment commentFormat // Bye-Bye %s()
+
+// Ok - below limit
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+}
+
+// Ok - has correct comment
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+ // Line 21
+ // Line 22
+ // Line 23
+ // Line 24
+ // Line 25
+ // Line 26
+ // Line 27
+ // Line 28
+ // Line 29
+ // Line 30
+}// Bye-Bye foreach()
+
+// This should be caught - wrong comment
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+ // Line 21
+ // Line 22
+ // Line 23
+ // Line 24
+ // Line 25
+ // Line 26
+ // Line 27
+ // Line 28
+ // Line 29
+ // Line 30
+}//end foreach
+
+// This should be caught - no comment
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+ // Line 21
+ // Line 22
+ // Line 23
+ // Line 24
+ // Line 25
+ // Line 26
+ // Line 27
+ // Line 28
+ // Line 29
+ // Line 30
+}
+
+// @codingStandardsChangeSetting Squiz.Commenting.LongConditionClosingComment lineLimit 20
+// @codingStandardsChangeSetting Squiz.Commenting.LongConditionClosingComment commentFormat //end %s
+
+try {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (DALException $e) {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (ChannelException $e) {
+ // some code here.
+} finally {
+ // some code here.
+}
--- /dev/null
+<?php
+
+function long_function()
+{
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+ }//end if
+
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+ }//end if
+
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+ } else {
+ // Short ELSE
+ }//end if
+
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+ } else {
+ // Short ELSE
+ }//end if
+}
+
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end foreach
+
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ foreach ($blah as $val) {
+ // Short foreach.
+ }
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end foreach
+
+for ($var =1; $var < 20; $var++) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end for
+
+for ($var =1; $var < 20; $var++) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ for ($val =1; $val < 20; $val++) {
+ // Short for.
+ }
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end for
+
+while ($var < 20) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end while
+
+while ($var < 20) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ while ($something) {
+ // Short while.
+ }
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end while
+
+if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+} //end if
+
+if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if ($variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+} else {
+ // Short ELSE
+} //end if
+
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} //end foreach
+
+for ($var =1; $var < 20; $var++) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} //end for
+
+while ($var < 20) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} //end while
+
+while ($var < 20) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end while
+
+if (true) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} else if ($condition) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} elseif ($cond) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} else {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end if
+
+if ($something) {
+ // Line 1
+ // Line 2
+} else if ($somethingElse) {
+ // Line 1
+ // Line 2
+} else {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end if
+
+switch ($something) {
+ case '1':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+ case '2':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+ case '3':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+ case '4':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+ case '5':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+}//end switch
+
+// Wrong comment
+if ($condition) {
+ echo "true";
+}//end if
+
+if ($condition) {
+ echo "true";
+} //end if
+
+try {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (Exception $e) {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+}//end try
+
+try {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (Exception $e) {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+}
+
+try {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (Exception $e) {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+}//end try
+
+try {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (DALException $e) {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (ChannelException $e) {
+ // some code here.
+} catch (Exception $e) {
+ // some code here.
+}//end try
+
+switch ($foo) {
+ case 'one' : {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ break;
+ }//end case
+ case 'one' :
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ break;
+}//end switch
+
+// Yes, code like this does exist.
+if ($foo) {
+ return $foo;
+} elseif ($bar)
+ return $bar;
+
+switch ($foo) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ case 40:
+ case 41:
+ case 42:
+ case 43:
+ case 44:
+ case 45:
+ case 46:
+ case 47:
+ case 48:
+ case 49:
+ case 50:
+ case 51:
+ case 52:
+ case 53:
+ case 54:
+ case 55:
+ case 56:
+ case 57:
+ case 58:
+ case 59:
+ echo $foo;
+ break;
+}//end switch
+
+// @codingStandardsChangeSetting Squiz.Commenting.LongConditionClosingComment lineLimit 5
+// @codingStandardsChangeSetting Squiz.Commenting.LongConditionClosingComment commentFormat //Dragonbait-%s
+
+function quite_long_function()
+{
+ // Ok - below limit
+ if ($longFunction) {
+ $variable = 'hello';
+ }
+
+ // Ok - correct comment
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ }//Dragonbait-if
+
+ // This should be caught - wrong comment
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+ } else {
+ // Short ELSE
+ }//Dragonbait-if
+
+ // This should be caught - no comment
+ if ($longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ $variable = 'hello';
+ } else {
+ // Short ELSE
+ }//Dragonbait-if
+}
+
+// @codingStandardsChangeSetting Squiz.Commenting.LongConditionClosingComment lineLimit 30
+// @codingStandardsChangeSetting Squiz.Commenting.LongConditionClosingComment commentFormat // Bye-Bye %s()
+
+// Ok - below limit
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+}
+
+// Ok - has correct comment
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+ // Line 21
+ // Line 22
+ // Line 23
+ // Line 24
+ // Line 25
+ // Line 26
+ // Line 27
+ // Line 28
+ // Line 29
+ // Line 30
+}// Bye-Bye foreach()
+
+// This should be caught - wrong comment
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+ // Line 21
+ // Line 22
+ // Line 23
+ // Line 24
+ // Line 25
+ // Line 26
+ // Line 27
+ // Line 28
+ // Line 29
+ // Line 30
+}// Bye-Bye foreach()
+
+// This should be caught - no comment
+foreach ($var as $val) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+ // Line 21
+ // Line 22
+ // Line 23
+ // Line 24
+ // Line 25
+ // Line 26
+ // Line 27
+ // Line 28
+ // Line 29
+ // Line 30
+}// Bye-Bye foreach()
+
+// @codingStandardsChangeSetting Squiz.Commenting.LongConditionClosingComment lineLimit 20
+// @codingStandardsChangeSetting Squiz.Commenting.LongConditionClosingComment commentFormat //end %s
+
+try {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (DALException $e) {
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+ // some code here.
+} catch (ChannelException $e) {
+ // some code here.
+} finally {
+ // some code here.
+}//end try
--- /dev/null
+function long_function()
+{
+ if (longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ variable = 'hello';
+
+ if (variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if (variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+ }//end if
+
+ if (longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ variable = 'hello';
+
+ if (variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if (variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+ }
+
+ if (longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ variable = 'hello';
+
+ if (variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if (variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+ } else {
+ // Short ELSE
+ }//end if
+
+ if (longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ variable = 'hello';
+
+ if (variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if (variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+ } else {
+ // Short ELSE
+ }
+}
+
+for (variable=1; variable < 20; variable++) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end for
+
+for (variable=1; variable < 20; variable++) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ for (val =1; val < 20; val++) {
+ // Short for.
+ }
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}
+
+while (variable < 20) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end while
+
+while (variable < 20) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ while (something) {
+ // Short while.
+ }
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}
+
+if (longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ variable = 'hello';
+
+ if (variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if (variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+} //end if
+
+if (longFunction) {
+ // This is a long
+ // IF statement
+ // that does
+ // not have
+ // an ELSE
+ // block on it
+ variable = 'hello';
+
+ if (variable === 'hello') {
+ // This is a short
+ // IF statement
+ }
+
+ if (variable === 'hello') {
+ // This is a short
+ // IF statement
+ } else {
+ // This is a short ELSE
+ // statement
+ }
+} else {
+ // Short ELSE
+} //end if
+
+for (variable=1; variable < 20; variable++) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} //end for
+
+while (variable < 20) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} //end while
+
+while (variable < 20) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end for
+
+if (true) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} else if (condition) {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+} else {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}//end if
+
+if (something) {
+ // Line 1
+ // Line 2
+} else if (somethingElse) {
+ // Line 1
+ // Line 2
+} else {
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ // Line 6
+ // Line 7
+ // Line 8
+ // Line 9
+ // Line 10
+ // Line 11
+ // Line 12
+ // Line 13
+ // Line 14
+ // Line 15
+ // Line 16
+ // Line 17
+ // Line 18
+ // Line 19
+ // Line 20
+}
+
+switch (something) {
+ case '1':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+ case '2':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+ case '3':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+ case '4':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+ case '5':
+ // Line 1
+ // Line 2
+ // Line 3
+ // Line 4
+ // Line 5
+ break;
+}
+
+// Wrong comment
+if (condition) {
+ condition = true;
+}//end foreach
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the LongConditionClosingComment 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\Standards\Squiz\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LongConditionClosingCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='LongConditionClosingCommentUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'LongConditionClosingCommentUnitTest.inc':
+ return array(
+ 49 => 1,
+ 99 => 1,
+ 146 => 1,
+ 192 => 1,
+ 215 => 1,
+ 238 => 1,
+ 261 => 1,
+ 286 => 1,
+ 309 => 1,
+ 332 => 1,
+ 355 => 1,
+ 378 => 1,
+ 493 => 1,
+ 531 => 1,
+ 536 => 1,
+ 540 => 1,
+ 562 => 1,
+ 601 => 1,
+ 629 => 1,
+ 663 => 1,
+ 765 => 1,
+ 798 => 1,
+ 811 => 1,
+ 897 => 1,
+ 931 => 1,
+ 962 => 1,
+ );
+ break;
+ case 'LongConditionClosingCommentUnitTest.js':
+ return array(
+ 47 => 1,
+ 97 => 1,
+ 144 => 1,
+ 190 => 1,
+ 213 => 1,
+ 238 => 1,
+ 261 => 1,
+ 284 => 1,
+ 307 => 1,
+ 401 => 1,
+ 439 => 1,
+ 444 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+function test()
+{
+ $string = 'hello';
+ $string = 'hello'; // Set string to hello.
+ // Valid comment.
+}
+
+function test() // This is a function
+{
+
+}//end test()
+
+
+class TestClass
+{
+ public $good = true; // Indeed.
+
+}//end class
+
+?>
\ No newline at end of file
--- /dev/null
+function test(id, buttons) // cool function
+{
+ alert('hello');
+ alert('hello again'); // And again.
+ // Valid comment.
+
+}//end test()
+
+var good = true; // Indeed.
+
+mig.Gallery.prototype = {
+
+ init: function(cb)
+ {
+
+ },//end init()
+
+ imageClicked: function(id)
+ {
+
+ }//end imageClicked()
+
+};
+
+dfx.getIframeDocument = function(iframe)
+{
+
+ return doc;
+
+};//end dfx.getIframeDocument()
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the PostStatementComment 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\Standards\Squiz\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class PostStatementCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='PostStatementCommentUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'PostStatementCommentUnitTest.inc':
+ return array(
+ 6 => 1,
+ 10 => 1,
+ 18 => 1,
+ );
+ break;
+ case 'PostStatementCommentUnitTest.js':
+ return array(
+ 1 => 1,
+ 4 => 1,
+ 9 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class VariableCommentUnitTest
+{
+
+ /**
+ * Short description of the member variable.
+ *
+ * Long description of member variable. Can span over multiple
+ * lines and can have multiple paragraphs.
+ *
+ * @var array
+ * @see otherFunctions()
+ * @see anotherFunctions()
+ */
+ public $variableName = array();
+
+
+ // Not "/**" style comment.
+ //
+ // @var string
+ private $_incorrectCommentStyle = '';
+
+
+ protected $noComment = '';
+
+
+ /**
+ *
+ * Extra newline before short comment.
+ *
+ * @var string
+ */
+ public $extraNewlineBeforeShort = '';
+
+
+ /**
+ * Extra newline between short description.
+ *
+ *
+ *
+ * Long description.
+ *
+ * @var string
+ */
+ private $_extraNewlineBetween = '';
+
+
+ /**
+ * Extra newline before tags.
+ *
+ * Long description,
+ * with two var tags.
+ *
+ *
+ * @var string
+ * @var array
+ */
+ protected $extraNewlineBeforeTags = '';
+
+
+ /**
+ * No newline before tags, var tag missing.
+ * @see otherFunctions()
+ */
+ protected $noNewlineBeforeTags = '';
+
+
+ /**
+ * Short comment that spans multiple
+ * lines and does not end with a full stop
+ * also var type is missing
+ *
+ * @var
+ */
+ public $singleLineFullStopShortComment = '';
+
+
+ /**
+ * Incorrect tag indentation.
+ *
+ * @var string
+ * @see otherFunctions()
+ * @see anotherFunctions()
+ * @see
+ */
+ public $singleLineFullStopShortComment = '';
+
+
+ /**
+ * Unknown summary tag
+ *
+ * @var string
+ * @summary unknown tag
+ */
+ public $missingSinceTag = '';
+
+
+ /**
+ * T_VARIABLE check, without scope.
+ *
+ * @var string
+ */
+ $variableCheck = '';
+
+ /**
+ * T_VARIABLE check, var in string and in function.
+ *
+ * @param integer $var1 First variable.
+ * @param integer $var2 Second variable.
+ *
+ * @return integer
+ */
+ protected function checkVariable($var1, $var2)
+ {
+ $var4 = 'A normal variable';
+ $var5 = PHP_CodeSniffer_Tokens::$operators;
+ echo "Printing $var1 $var2 and unknown $var3 in a double quoted string\n";
+ foreach (self::$_array as $index => $content) {
+ echo $content;
+ }
+
+ return $var1;
+
+ }//end checkVariable()
+
+
+ /**
+ *
+ *
+ */
+ $emptyVarDoc = '';
+
+ /**
+ * Var type checking (int v.s. integer).
+ *
+ * @var int
+ */
+ private $_varSimpleTypeCheck;
+
+
+ /**
+ * Var type checking (array(int => string) v.s. array(int => string)).
+ *
+ * @var array(int => string)
+ */
+ private $_varArrayTypeCheck;
+
+
+ /**
+ * Boolean @var tag Capitalized
+ *
+ * @var Boolean
+ */
+ public $CapBoolTag = true;
+
+
+ /**
+ * Boolean @var tag Capitalized
+ *
+ * @var BOOLEAN
+ */
+ public $CapBoolTag2 = true;
+
+
+ /**
+ * Double @var tag Capitalized
+ *
+ * @var Double
+ */
+ public $CapDoubleTag = 1;
+
+
+ /**
+ * Double @var tag Capitalized
+ *
+ * @var DOUBLE
+ */
+ public $CapDoubleTag2 = 1;
+
+
+ /**
+ * Real @var tag Capitalized
+ *
+ * @var Real
+ */
+ public $CapRealTag = 1;
+
+
+ /**
+ * Real @var tag Capitalized
+ *
+ * @var REAL
+ */
+ public $CapRealTag2 = 1;
+
+
+ /**
+ * Float @var tag Capitalized
+ *
+ * @var Float
+ */
+ public $CapFloatTag = 1;
+
+
+ /**
+ * Float @var tag Capitalized
+ *
+ * @var FLOAT
+ */
+ public $CapFloatTag2 = 1;
+
+
+ /**
+ * Int @var tag Capitalized
+ *
+ * @var Int
+ */
+ public $CapIntTag = 1;
+
+
+ /**
+ * Int @var tag Capitalized
+ *
+ * @var INT
+ */
+ public $CapIntTag2 = 1;
+
+
+ /**
+ * Integer @var tag Capitalized
+ *
+ * @var Integer
+ */
+ public $CapIntTag3 = 1;
+
+
+ /**
+ * Integer @var tag Capitalized
+ *
+ * @var INTEGER
+ */
+ public $CapIntTag4 = 1;
+
+
+ /**
+ * Array @var tag Capitalized
+ *
+ * @var Array
+ */
+ public $CapVarTag = [];
+
+
+ /**
+ * Array @var tag All Caps
+ *
+ * @var ARRAY
+ */
+ public $CapVarTag2 = [];
+
+
+ /**
+ * Array @var tag Capitalized
+ *
+ * @var Array()
+ */
+ public $CapVarTag3 = [];
+
+
+ /**
+ * Array @var tag All Caps
+ *
+ * @var ARRAY()
+ */
+ public $CapVarTag4 = [];
+
+
+ /**
+ * Var type checking (STRING v.s. string).
+ *
+ * @var STRING
+ */
+ private $_varCaseTypeCheck;
+
+
+ /**
+ * @var integer
+ */
+ private $_varWithNoShortComment;
+
+ protected $noComment2 = '';
+
+
+}//end class
+
+
+/**
+ * VariableCommentUnitTest2.
+ *
+ * Long description goes here.
+ *
+ */
+class VariableCommentUnitTest2
+{
+
+ public $hello;
+
+ /** Comment starts here.
+ *
+ * @var string
+ *
+ */
+ private $_varCaseTypeCheck;
+
+ /**
+ * 这是一条测试评论.
+ *
+ * @var string
+ */
+ public $foo;
+
+}//end class
+
+
+/*
+ * Class comment
+ */
+class Foo
+{
+
+ protected $bar;
+
+ /**
+ * Short description of the member variable.
+ *
+ * @var array
+ */
+ public static $variableName = array();
+
+}
--- /dev/null
+<?php
+class VariableCommentUnitTest
+{
+
+ /**
+ * Short description of the member variable.
+ *
+ * Long description of member variable. Can span over multiple
+ * lines and can have multiple paragraphs.
+ *
+ * @var array
+ * @see otherFunctions()
+ * @see anotherFunctions()
+ */
+ public $variableName = array();
+
+
+ // Not "/**" style comment.
+ //
+ // @var string
+ private $_incorrectCommentStyle = '';
+
+
+ protected $noComment = '';
+
+
+ /**
+ *
+ * Extra newline before short comment.
+ *
+ * @var string
+ */
+ public $extraNewlineBeforeShort = '';
+
+
+ /**
+ * Extra newline between short description.
+ *
+ *
+ *
+ * Long description.
+ *
+ * @var string
+ */
+ private $_extraNewlineBetween = '';
+
+
+ /**
+ * Extra newline before tags.
+ *
+ * Long description,
+ * with two var tags.
+ *
+ *
+ * @var string
+ * @var array
+ */
+ protected $extraNewlineBeforeTags = '';
+
+
+ /**
+ * No newline before tags, var tag missing.
+ * @see otherFunctions()
+ */
+ protected $noNewlineBeforeTags = '';
+
+
+ /**
+ * Short comment that spans multiple
+ * lines and does not end with a full stop
+ * also var type is missing
+ *
+ * @var
+ */
+ public $singleLineFullStopShortComment = '';
+
+
+ /**
+ * Incorrect tag indentation.
+ *
+ * @var string
+ * @see otherFunctions()
+ * @see anotherFunctions()
+ * @see
+ */
+ public $singleLineFullStopShortComment = '';
+
+
+ /**
+ * Unknown summary tag
+ *
+ * @var string
+ * @summary unknown tag
+ */
+ public $missingSinceTag = '';
+
+
+ /**
+ * T_VARIABLE check, without scope.
+ *
+ * @var string
+ */
+ $variableCheck = '';
+
+ /**
+ * T_VARIABLE check, var in string and in function.
+ *
+ * @param integer $var1 First variable.
+ * @param integer $var2 Second variable.
+ *
+ * @return integer
+ */
+ protected function checkVariable($var1, $var2)
+ {
+ $var4 = 'A normal variable';
+ $var5 = PHP_CodeSniffer_Tokens::$operators;
+ echo "Printing $var1 $var2 and unknown $var3 in a double quoted string\n";
+ foreach (self::$_array as $index => $content) {
+ echo $content;
+ }
+
+ return $var1;
+
+ }//end checkVariable()
+
+
+ /**
+ *
+ *
+ */
+ $emptyVarDoc = '';
+
+ /**
+ * Var type checking (int v.s. integer).
+ *
+ * @var integer
+ */
+ private $_varSimpleTypeCheck;
+
+
+ /**
+ * Var type checking (array(int => string) v.s. array(int => string)).
+ *
+ * @var array(integer => string)
+ */
+ private $_varArrayTypeCheck;
+
+
+ /**
+ * Boolean @var tag Capitalized
+ *
+ * @var boolean
+ */
+ public $CapBoolTag = true;
+
+
+ /**
+ * Boolean @var tag Capitalized
+ *
+ * @var boolean
+ */
+ public $CapBoolTag2 = true;
+
+
+ /**
+ * Double @var tag Capitalized
+ *
+ * @var float
+ */
+ public $CapDoubleTag = 1;
+
+
+ /**
+ * Double @var tag Capitalized
+ *
+ * @var float
+ */
+ public $CapDoubleTag2 = 1;
+
+
+ /**
+ * Real @var tag Capitalized
+ *
+ * @var float
+ */
+ public $CapRealTag = 1;
+
+
+ /**
+ * Real @var tag Capitalized
+ *
+ * @var float
+ */
+ public $CapRealTag2 = 1;
+
+
+ /**
+ * Float @var tag Capitalized
+ *
+ * @var float
+ */
+ public $CapFloatTag = 1;
+
+
+ /**
+ * Float @var tag Capitalized
+ *
+ * @var float
+ */
+ public $CapFloatTag2 = 1;
+
+
+ /**
+ * Int @var tag Capitalized
+ *
+ * @var integer
+ */
+ public $CapIntTag = 1;
+
+
+ /**
+ * Int @var tag Capitalized
+ *
+ * @var integer
+ */
+ public $CapIntTag2 = 1;
+
+
+ /**
+ * Integer @var tag Capitalized
+ *
+ * @var integer
+ */
+ public $CapIntTag3 = 1;
+
+
+ /**
+ * Integer @var tag Capitalized
+ *
+ * @var integer
+ */
+ public $CapIntTag4 = 1;
+
+
+ /**
+ * Array @var tag Capitalized
+ *
+ * @var array
+ */
+ public $CapVarTag = [];
+
+
+ /**
+ * Array @var tag All Caps
+ *
+ * @var array
+ */
+ public $CapVarTag2 = [];
+
+
+ /**
+ * Array @var tag Capitalized
+ *
+ * @var array
+ */
+ public $CapVarTag3 = [];
+
+
+ /**
+ * Array @var tag All Caps
+ *
+ * @var array
+ */
+ public $CapVarTag4 = [];
+
+
+ /**
+ * Var type checking (STRING v.s. string).
+ *
+ * @var string
+ */
+ private $_varCaseTypeCheck;
+
+
+ /**
+ * @var integer
+ */
+ private $_varWithNoShortComment;
+
+ protected $noComment2 = '';
+
+
+}//end class
+
+
+/**
+ * VariableCommentUnitTest2.
+ *
+ * Long description goes here.
+ *
+ */
+class VariableCommentUnitTest2
+{
+
+ public $hello;
+
+ /** Comment starts here.
+ *
+ * @var string
+ *
+ */
+ private $_varCaseTypeCheck;
+
+ /**
+ * 这是一条测试评论.
+ *
+ * @var string
+ */
+ public $foo;
+
+}//end class
+
+
+/*
+ * Class comment
+ */
+class Foo
+{
+
+ protected $bar;
+
+ /**
+ * Short description of the member variable.
+ *
+ * @var array
+ */
+ public static $variableName = array();
+
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the VariableComment 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\Standards\Squiz\Tests\Commenting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class VariableCommentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 21 => 1,
+ 24 => 1,
+ 56 => 1,
+ 64 => 1,
+ 73 => 1,
+ 84 => 1,
+ 130 => 1,
+ 136 => 1,
+ 144 => 1,
+ 152 => 1,
+ 160 => 1,
+ 168 => 1,
+ 176 => 1,
+ 184 => 1,
+ 192 => 1,
+ 200 => 1,
+ 208 => 1,
+ 216 => 1,
+ 224 => 1,
+ 232 => 1,
+ 240 => 1,
+ 248 => 1,
+ 256 => 1,
+ 264 => 1,
+ 272 => 1,
+ 280 => 1,
+ 290 => 1,
+ 305 => 1,
+ 330 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(93 => 1);
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$i = 0;
+do {
+ echo $i;
+} while ($i > 0);
+
+do
+{
+ echo $i;
+} while ($i > 0);
+
+do
+{
+ echo $i;
+}
+while ($i > 0);
+
+do { echo $i; } while ($i > 0);
+
+do{
+ echo $i;
+}while($i > 0);
+
+while ($i < 1) {
+ echo $i;
+}
+
+while($i < 1){
+ echo $i;
+}
+
+while ($i < 1) { echo $i; }
+
+for ($i = 1; $i < 1; $i++) {
+ echo $i;
+}
+
+for($i = 1; $i < 1; $i++){
+ echo $i;
+}
+
+for ($i = 1; $i < 1; $i++) { echo $i; }
+
+if ($i == 0) {
+ $i = 1;
+}
+
+if($i == 0){
+ $i = 1;
+}
+
+if ($i == 0) { $i = 1; }
+
+if ($i == 0) {
+ $i = 1;
+} else {
+ $i = 0;
+}
+
+if ($i == 0) {
+ $i = 1;
+}else{
+ $i = 0;
+}
+
+if ($i == 0) { $i = 1; } else { $i = 0; }
+
+if ($i == 0) {
+ $i = 1;
+} else if ($i == 2) {
+ $i = 0;
+}
+
+if ($i == 0) {
+ $i = 1;
+}else if($i == 2){
+ $i = 0;
+}
+
+if ($i == 0) { $i = 1; } else if ($i == 2) { $i = 0; }
+
+if ($i == 0) { // comments are allowed
+ $i = 1;
+}
+
+if ($i == 0) {// comments are allowed
+ $i = 1;
+}
+
+if ($i == 0) { /* comments are allowed*/
+ $i = 1;
+}
+
+if ($i == 0)
+{ // this is ok
+ $i = 1;
+}
+
+if ($i == 0) /* this is ok */ {
+}
+
+try {
+ $code = 'this';
+} catch (Exception $e) {
+ // Caught!
+}
+
+try { $code = 'this'; } catch (Exception $e) {
+ // Caught!
+}
+
+do { echo $i;
+} while ($i > 0);
+
+if ($i === 0) {
+
+ $i = 1
+}
+
+if ($a) {
+
+}
+elseif ($b) {
+}
+
+foreach ($items as $item) {
+ echo $item;
+}
+
+foreach($items as $item){
+ echo $item;
+}
+
+if ($a && $b) // && $c)
+{
+}
+
+if ($a == 5) :
+ echo "a equals 5";
+ echo "...";
+elseif ($a == 6) :
+ echo "a equals 6";
+ echo "!!!";
+else :
+ echo "a is neither 5 nor 6";
+endif;
+
+try {
+ // try body
+}
+catch (FirstExceptionType $e) {
+ // catch body
+}
+catch (OtherExceptionType $e) {
+ // catch body
+}
+
+switch($foo) {
+
+ case 'bar':
+ break;
+
+}
+
+if ($foo) :
+endif;
+
+?>
+
+<?php while($row = $data->getRow()): ?>
+ <p><?= $val ?></p>
+<?php endwhile; ?>
+
+<?php
+if ($foo === 1) {
+ ?>
+ <table><tr><td>
+ <?php
+ echo '2';
+ ?>
+ </td></tr></table>
+ <?php
+}
+
+if ($test) { /*one space after the bracket*/
+ echo 'true';
+}
+
+?>
+<?php foreach($formset['Fieldset'] as $fieldset): ?>
+ <?php foreach($fieldset['Field'] as $field): ?>
+ <?php endforeach; ?>
+<?php endforeach; ?>
+
+<?php foreach ($formset['Fieldset'] as $fieldset) : ?> hello
+<?php endforeach; ?>
+
+<?php foreach ($formset['Fieldset'] as $fieldset) :
+ ?>
+ hello
+<?php endforeach; ?>
+
+<?php
+$a = 1; $b = true;
+if (1 === $a) :
+ if ($b) {
+ echo 'b';
+ } else {
+ echo 'not b';
+ }
+else :
+ echo 'not 1';
+endif;
--- /dev/null
+<?php
+$i = 0;
+do {
+ echo $i;
+} while ($i > 0);
+
+do {
+ echo $i;
+} while ($i > 0);
+
+do {
+ echo $i;
+} while ($i > 0);
+
+do {
+echo $i; } while ($i > 0);
+
+do {
+ echo $i;
+} while ($i > 0);
+
+while ($i < 1) {
+ echo $i;
+}
+
+while ($i < 1) {
+ echo $i;
+}
+
+while ($i < 1) {
+echo $i; }
+
+for ($i = 1; $i < 1; $i++) {
+ echo $i;
+}
+
+for ($i = 1; $i < 1; $i++) {
+ echo $i;
+}
+
+for ($i = 1; $i < 1; $i++) {
+echo $i; }
+
+if ($i == 0) {
+ $i = 1;
+}
+
+if ($i == 0) {
+ $i = 1;
+}
+
+if ($i == 0) {
+$i = 1; }
+
+if ($i == 0) {
+ $i = 1;
+} else {
+ $i = 0;
+}
+
+if ($i == 0) {
+ $i = 1;
+} else {
+ $i = 0;
+}
+
+if ($i == 0) {
+$i = 1; } else {
+$i = 0; }
+
+if ($i == 0) {
+ $i = 1;
+} else if ($i == 2) {
+ $i = 0;
+}
+
+if ($i == 0) {
+ $i = 1;
+} else if ($i == 2) {
+ $i = 0;
+}
+
+if ($i == 0) {
+$i = 1; } else if ($i == 2) {
+$i = 0; }
+
+if ($i == 0) { // comments are allowed
+ $i = 1;
+}
+
+if ($i == 0) {// comments are allowed
+ $i = 1;
+}
+
+if ($i == 0) { /* comments are allowed*/
+ $i = 1;
+}
+
+if ($i == 0) { // this is ok
+ $i = 1;
+}
+
+if ($i == 0) { /* this is ok */
+}
+
+try {
+ $code = 'this';
+} catch (Exception $e) {
+ // Caught!
+}
+
+try {
+$code = 'this'; } catch (Exception $e) {
+ // Caught!
+}
+
+do {
+echo $i;
+} while ($i > 0);
+
+if ($i === 0) {
+
+ $i = 1
+}
+
+if ($a) {
+
+} elseif ($b) {
+}
+
+foreach ($items as $item) {
+ echo $item;
+}
+
+foreach ($items as $item) {
+ echo $item;
+}
+
+if ($a && $b) { // && $c)
+}
+
+if ($a == 5) :
+ echo "a equals 5";
+ echo "...";
+elseif ($a == 6) :
+ echo "a equals 6";
+ echo "!!!";
+else :
+ echo "a is neither 5 nor 6";
+endif;
+
+try {
+ // try body
+} catch (FirstExceptionType $e) {
+ // catch body
+} catch (OtherExceptionType $e) {
+ // catch body
+}
+
+switch ($foo) {
+
+ case 'bar':
+ break;
+
+}
+
+if ($foo) :
+endif;
+
+?>
+
+<?php while ($row = $data->getRow()) : ?>
+ <p><?= $val ?></p>
+<?php endwhile; ?>
+
+<?php
+if ($foo === 1) {
+ ?>
+ <table><tr><td>
+ <?php
+ echo '2';
+ ?>
+ </td></tr></table>
+ <?php
+}
+
+if ($test) { /*one space after the bracket*/
+ echo 'true';
+}
+
+?>
+<?php foreach ($formset['Fieldset'] as $fieldset) : ?>
+ <?php foreach ($fieldset['Field'] as $field) : ?>
+ <?php endforeach; ?>
+<?php endforeach; ?>
+
+<?php foreach ($formset['Fieldset'] as $fieldset) :
+?> hello
+<?php endforeach; ?>
+
+<?php foreach ($formset['Fieldset'] as $fieldset) :
+ ?>
+ hello
+<?php endforeach; ?>
+
+<?php
+$a = 1; $b = true;
+if (1 === $a) :
+ if ($b) {
+ echo 'b';
+ } else {
+ echo 'not b';
+ }
+else :
+ echo 'not 1';
+endif;
--- /dev/null
+
+i = 0;
+do {
+ i = 0;
+} while (i > 0);
+
+do
+{
+ i = 0;
+} while (i > 0);
+
+do
+{
+ i = 0;
+}
+while (i > 0);
+
+do { i = 0; } while (i > 0);
+
+do{
+ i = 0;
+}while(i > 0);
+
+while (i < 1) {
+ i = 0;
+}
+
+while(i < 1){
+ i = 0;
+}
+
+while (i < 1) { i = 0; }
+
+for (i = 1; i < 1; i++) {
+ i = 0;
+}
+
+for(i = 1; i < 1; i++){
+ i = 0;
+}
+
+for (i = 1; i < 1; i++) { i = 0; }
+
+if (i == 0) {
+ i = 1;
+}
+
+if(i == 0){
+ i = 1;
+}
+
+if (i == 0) { i = 1; }
+
+if (i == 0) {
+ i = 1;
+} else {
+ i = 0;
+}
+
+if (i == 0) {
+ i = 1;
+}else{
+ i = 0;
+}
+
+if (i == 0) { i = 1; } else { i = 0; }
+
+if (i == 0) {
+ i = 1;
+} else if (i == 2) {
+ i = 0;
+}
+
+if (i == 0) {
+ i = 1;
+}else if(i == 2){
+ i = 0;
+}
+
+if (i == 0) { i = 1; } else if (i == 2) { i = 0; }
+
+if (i == 0) { // comments are allowed
+ i = 1;
+}
+
+if (i == 0) {// comments are allowed
+ i = 1;
+}
+
+if (i == 0) { /* comments are allowed*/
+ i = 1;
+}
+
+if (i == 0)
+{ // this is ok
+ i = 1;
+}
+
+if (i == 0) /* this is ok */ {
+}
+
+try {
+ code = 'this';
+} catch (e) {
+ // Caught!
+}
+
+try { code = 'this'; } catch (e) {
+ // Caught!
+}
+
+do { i = 0;
+} while (i > 0);
+
+if (i === 0) {
+
+ i = 1
+}
+
+if (window.jQuery)(function($) {
+ $.fn.reset = function() {
+ return this.each(function() {
+ try {
+ this.reset();
+ } catch (e) {
+ }
+ });
+ };
+})(jQuery);
+
+if ($("#myid").rotationDegrees()=='90')
+ $('.modal').css({'transform': 'rotate(90deg)'});
+
+if ($("#myid").rotationDegrees()=='90')
+ $foo = {'transform': 'rotate(90deg)'};
--- /dev/null
+
+i = 0;
+do {
+ i = 0;
+} while (i > 0);
+
+do {
+ i = 0;
+} while (i > 0);
+
+do {
+ i = 0;
+} while (i > 0);
+
+do {
+i = 0; } while (i > 0);
+
+do {
+ i = 0;
+} while (i > 0);
+
+while (i < 1) {
+ i = 0;
+}
+
+while (i < 1) {
+ i = 0;
+}
+
+while (i < 1) {
+i = 0; }
+
+for (i = 1; i < 1; i++) {
+ i = 0;
+}
+
+for (i = 1; i < 1; i++) {
+ i = 0;
+}
+
+for (i = 1; i < 1; i++) {
+i = 0; }
+
+if (i == 0) {
+ i = 1;
+}
+
+if (i == 0) {
+ i = 1;
+}
+
+if (i == 0) {
+i = 1; }
+
+if (i == 0) {
+ i = 1;
+} else {
+ i = 0;
+}
+
+if (i == 0) {
+ i = 1;
+} else {
+ i = 0;
+}
+
+if (i == 0) {
+i = 1; } else {
+i = 0; }
+
+if (i == 0) {
+ i = 1;
+} else if (i == 2) {
+ i = 0;
+}
+
+if (i == 0) {
+ i = 1;
+} else if (i == 2) {
+ i = 0;
+}
+
+if (i == 0) {
+i = 1; } else if (i == 2) {
+i = 0; }
+
+if (i == 0) { // comments are allowed
+ i = 1;
+}
+
+if (i == 0) {// comments are allowed
+ i = 1;
+}
+
+if (i == 0) { /* comments are allowed*/
+ i = 1;
+}
+
+if (i == 0) { // this is ok
+ i = 1;
+}
+
+if (i == 0) { /* this is ok */
+}
+
+try {
+ code = 'this';
+} catch (e) {
+ // Caught!
+}
+
+try {
+code = 'this'; } catch (e) {
+ // Caught!
+}
+
+do {
+i = 0;
+} while (i > 0);
+
+if (i === 0) {
+
+ i = 1
+}
+
+if (window.jQuery)(function($) {
+ $.fn.reset = function() {
+ return this.each(function() {
+ try {
+ this.reset();
+ } catch (e) {
+ }
+ });
+ };
+})(jQuery);
+
+if ($("#myid").rotationDegrees()=='90')
+ $('.modal').css({'transform': 'rotate(90deg)'});
+
+if ($("#myid").rotationDegrees()=='90')
+ $foo = {'transform': 'rotate(90deg)'};
--- /dev/null
+<?php
+/**
+ * Unit test class for the ControlSignature 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\Standards\Squiz\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ControlSignatureUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='ControlSignatureUnitTest.inc')
+ {
+ $errors = array(
+ 7 => 1,
+ 12 => 1,
+ 15 => 1,
+ 18 => 1,
+ 20 => 1,
+ 22 => 2,
+ 28 => 2,
+ 32 => 1,
+ 38 => 2,
+ 42 => 1,
+ 48 => 2,
+ 52 => 1,
+ 62 => 2,
+ 66 => 2,
+ 76 => 4,
+ 80 => 2,
+ 94 => 1,
+ 99 => 1,
+ 108 => 1,
+ 112 => 1,
+ );
+
+ if ($testFile === 'ControlSignatureUnitTest.inc') {
+ $errors[122] = 1;
+ $errors[130] = 2;
+ $errors[134] = 1;
+ $errors[150] = 1;
+ $errors[153] = 1;
+ $errors[158] = 1;
+ $errors[165] = 1;
+ $errors[170] = 2;
+ $errors[185] = 1;
+ $errors[190] = 2;
+ $errors[191] = 2;
+ $errors[195] = 1;
+ }
+
+ return $errors;
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+if ($something) {
+} else if ($somethingElse) {
+}
+
+if ($something) {
+} elseif ($somethingElse) {
+}
+
+if ($something) {
+} else if ($somethingElse) {
+} elseif ($another) {
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ElseIfDeclaration 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\Standards\Squiz\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ElseIfDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 8 => 1,
+ 13 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// Valid.
+foreach ($something as $blah => $that) {
+}
+
+// Invalid.
+foreach ( $something as $blah => $that ) {
+}
+
+foreach ($something as $blah => $that) {
+}
+
+foreach ($something as $blah => $that) {
+}
+
+foreach (${something}AS$blah=>$that) {
+}
+
+// The works.
+foreach ( $something aS $blah => $that ) {
+}
+
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForEachLoopDeclaration requiredSpacesAfterOpen 1
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForEachLoopDeclaration requiredSpacesBeforeClose 1
+foreach ($something as $blah => $that) {}
+foreach ( $something as $blah => $that ) {}
+foreach ( $something as $blah => $that ) {}
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForEachLoopDeclaration requiredSpacesAfterOpen 0
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForEachLoopDeclaration requiredSpacesBeforeClose 0
+
+foreach ([
+ 'foo' => 'bar',
+ 'foobaz' => 'bazzy',
+ ] as $key => $value) {
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+// Valid.
+foreach ($something as $blah => $that) {
+}
+
+// Invalid.
+foreach ($something as $blah => $that) {
+}
+
+foreach ($something as $blah => $that) {
+}
+
+foreach ($something as $blah => $that) {
+}
+
+foreach (${something} as $blah => $that) {
+}
+
+// The works.
+foreach ($something as $blah => $that) {
+}
+
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForEachLoopDeclaration requiredSpacesAfterOpen 1
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForEachLoopDeclaration requiredSpacesBeforeClose 1
+foreach ( $something as $blah => $that ) {}
+foreach ( $something as $blah => $that ) {}
+foreach ( $something as $blah => $that ) {}
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForEachLoopDeclaration requiredSpacesAfterOpen 0
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForEachLoopDeclaration requiredSpacesBeforeClose 0
+
+foreach ([
+ 'foo' => 'bar',
+ 'foobaz' => 'bazzy',
+ ] as $key => $value) {
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ForEachLoopDeclaration 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\Standards\Squiz\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ForEachLoopDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 8 => 2,
+ 11 => 2,
+ 14 => 2,
+ 17 => 5,
+ 21 => 7,
+ 26 => 2,
+ 28 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// Valid.
+for ($i = 0; $i < 10; $i++) {
+}
+
+// Invalid.
+for ( $i = 0; $i < 10; $i++ ) {
+}
+
+for ($i = 0; $i < 10; $i++) {
+}
+
+for ($i = 0 ; $i < 10 ; $i++) {
+}
+
+for ($i = 0;$i < 10;$i++) {
+}
+
+// The works.
+for ( $i = 0 ; $i < 10 ; $i++ ) {
+}
+
+for ($i = 0; $i < 10;) {
+}
+
+for ($i = 0; $i < 10; ) {
+}
+
+for ($i = 0; ; $i++) {
+}
+for ($i = 0;; $i++) {
+}
+
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForLoopDeclaration requiredSpacesAfterOpen 1
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForLoopDeclaration requiredSpacesBeforeClose 1
+for ($i = 0; $i < 10; $i++) {}
+for ( $i = 0; $i < 10; $i++ ) {}
+for ( $i = 0; $i < 10; $i++ ) {}
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForLoopDeclaration requiredSpacesAfterOpen 0
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForLoopDeclaration requiredSpacesBeforeClose 0
--- /dev/null
+// Valid.
+for (var i = 0; i < 10; i++) {
+}
+
+// Invalid.
+for ( i = 0; i < 10; i++ ) {
+}
+
+for (i = 0; i < 10; i++) {
+}
+
+for (var i = 0 ; i < 10 ; i++) {
+}
+
+for (i = 0;i < 10;i++) {
+}
+
+// The works.
+for ( var i = 0 ; i < 10 ; i++ ) {
+}
+
+this.formats = {};
+dfx.inherits('ContentFormat', 'Widget');
+
+for (var widgetid in this.loadedContents) {
+ if (dfx.isset(widget) === true) {
+ widget.loadAutoSaveCWidgetStore.setData('activeScreen', null);widget.getContents(this.loadedContents[widgetid], function() {self.widgetLoaded(widget.id);});
+ }
+}
+
+for (var i = 0; i < 10;) {
+}
+for (var i = 0; i < 10; ) {
+}
+
+for (var i = 0; ; i++) {
+}
+for (var i = 0;; i++) {
+}
+
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForLoopDeclaration requiredSpacesAfterOpen 1
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForLoopDeclaration requiredSpacesBeforeClose 1
+for (var i = 0; i < 10; i++) {}
+for ( var i = 0; i < 10; i++ ) {}
+for ( var i = 0; i < 10; i++ ) {}
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForLoopDeclaration requiredSpacesAfterOpen 0
+// @codingStandardsChangeSetting Squiz.ControlStructures.ForLoopDeclaration requiredSpacesBeforeClose 0
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ForLoopDeclaration 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\Standards\Squiz\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ForLoopDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='ForLoopDeclarationUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'ForLoopDeclarationUnitTest.inc':
+ return array(
+ 8 => 2,
+ 11 => 2,
+ 14 => 2,
+ 17 => 2,
+ 21 => 6,
+ 27 => 1,
+ 30 => 1,
+ 37 => 2,
+ 39 => 2,
+ );
+ break;
+ case 'ForLoopDeclarationUnitTest.js':
+ return array(
+ 6 => 2,
+ 9 => 2,
+ 12 => 2,
+ 15 => 2,
+ 19 => 6,
+ 33 => 1,
+ 36 => 1,
+ 43 => 2,
+ 45 => 2,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+echo 'var is '.(($var < 0) ? 'negative' : 'positive');
+echo 'var is '.($var < 0 ? 'negative' : 'positive');
+echo 'var is '.(($var < 0) ? 'negative' :'positive');
+echo 'var is '.(($var < 0) ? 'negative': 'positive');
+echo 'var is '.(($var < 0) ? 'negative' : 'positive');
+echo 'var is '.(($var < 0) ? 'negative' : 'positive');
+echo 'var is '.(($var < 0) ? 'negative' : 'positive');
+echo 'var is '.(($var < 0) ? 'negative' : 'positive');
+
+echo 'var is '.(($var < 0)
+ ? 'negative'
+ : 'positive');
+
+$args = array(
+ '"'.$this->id.'"',
+ '"'.$this->stepInfo['title'].'"',
+ '"'.((isset($this->stepInfo['description']) === TRUE) ? $this->stepInfo['description'] : '').'"',
+ '"'.(isset($this->stepInfo['description']) === TRUE ? $this->stepInfo['description'] : '').'"',
+ '"'.$this->stepInfo['title'].'"',
+ );
+
+echo (TRUE)?'Hello':'Bye';
+
+$array = array(
+ 'one' => ($test == 1) ? true : false,
+ 'two' => (($test == 1) ? true : false),
+ 'three' => (($test == 1) ? true : false)
+);
+$var = ($test == 1) ? true : false;
+$var = (myFunc(1,2,3) == 1) ? true : false;
+
+set('config', function() {
+ $foo = ($bar === "on") ? "1" : "2";
+});
+
+$config = function() {
+ $foo = ($bar === "on") ? "1" : "2";
+};
+
+rand(0, 1) ? 'ěščřžýáí' : NULL;
+
+$c = ($argv[1]) ? : "";
+$filepath = realpath($argv[1]) ?: $argv[1];
+$c = ($argv[1]) ? /* comment */ : "";
+$c = ($argv[1]) ?
+: "";
--- /dev/null
+<?php
+
+echo 'var is '.(($var < 0) ? 'negative' : 'positive');
+echo 'var is '.($var < 0 ? 'negative' : 'positive');
+echo 'var is '.(($var < 0) ? 'negative' : 'positive');
+echo 'var is '.(($var < 0) ? 'negative' : 'positive');
+echo 'var is '.(($var < 0) ? 'negative' : 'positive');
+echo 'var is '.(($var < 0) ? 'negative' : 'positive');
+echo 'var is '.(($var < 0) ? 'negative' : 'positive');
+echo 'var is '.(($var < 0) ? 'negative' : 'positive');
+
+echo 'var is '.(($var < 0)
+ ? 'negative'
+ : 'positive');
+
+$args = array(
+ '"'.$this->id.'"',
+ '"'.$this->stepInfo['title'].'"',
+ '"'.((isset($this->stepInfo['description']) === TRUE) ? $this->stepInfo['description'] : '').'"',
+ '"'.(isset($this->stepInfo['description']) === TRUE ? $this->stepInfo['description'] : '').'"',
+ '"'.$this->stepInfo['title'].'"',
+ );
+
+echo (TRUE) ? 'Hello' : 'Bye';
+
+$array = array(
+ 'one' => ($test == 1) ? true : false,
+ 'two' => (($test == 1) ? true : false),
+ 'three' => (($test == 1) ? true : false)
+);
+$var = ($test == 1) ? true : false;
+$var = (myFunc(1,2,3) == 1) ? true : false;
+
+set('config', function() {
+ $foo = ($bar === "on") ? "1" : "2";
+});
+
+$config = function() {
+ $foo = ($bar === "on") ? "1" : "2";
+};
+
+rand(0, 1) ? 'ěščřžýáí' : NULL;
+
+$c = ($argv[1]) ?: "";
+$filepath = realpath($argv[1]) ?: $argv[1];
+$c = ($argv[1]) ? /* comment */ : "";
+$c = ($argv[1]) ?
+: "";
--- /dev/null
+<?php
+/**
+ * Unit test class for the InlineIfDeclaration 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\Standards\Squiz\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class InlineIfDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Get a list of CLI values to set befor the file is tested.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array
+ */
+ public function getCliValues($testFile)
+ {
+ return array('--encoding=utf-8');
+
+ }//end getCliValues()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 4 => 1,
+ 5 => 1,
+ 6 => 1,
+ 7 => 1,
+ 8 => 1,
+ 9 => 1,
+ 10 => 1,
+ 13 => 1,
+ 20 => 1,
+ 24 => 4,
+ 44 => 1,
+ 47 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+Foreach ($condition as $cond) {}
+
+FOR ($i = 1; $i < 10; $i++) {}
+
+wHile ($i < 10) {}
+
+DO {
+} While ($1 < 10)
+
+Switch ($condition) {}
+
+If ($condition) {
+} elsE if ($condition) {
+} elseIf ($condition) {
+} else {
+}
+
+TRY {
+} Catch (Exception $e) {
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the LowercaseDeclaration 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\Standards\Squiz\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LowercaseDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 5 => 1,
+ 7 => 1,
+ 9 => 1,
+ 10 => 1,
+ 12 => 1,
+ 14 => 1,
+ 15 => 1,
+ 16 => 1,
+ 20 => 1,
+ 21 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+// Valid SWITCH statement.
+switch ($something) {
+ case '1':
+ $case = '1';
+ break;
+
+ case '2':
+ case '3':
+ $case = '5';
+ break;
+
+ case '4':
+ $case = '4';
+ break;
+
+ default:
+ $case = null;
+ break;
+}
+
+// Alignment wrong.
+switch ($something) {
+ case '1':
+ $case = '1';
+ return '1';
+
+case '2':
+ case '3':
+ $case = '5';
+ break;
+
+case '4':
+ $case = '4';
+break;
+
+ default:
+ $case = null;
+ break;
+}
+
+// Closing brace wrong.
+switch ($something) {
+ case '1':
+ $case = '1';
+ break;
+ }
+
+// PEAR style.
+switch ($something) {
+case '1':
+ $case = '1';
+ break;
+case '2':
+case '3':
+ $case = '5';
+ break;
+case '4':
+ $case = '4';
+ break;
+default:
+ $case = null;
+ break;
+}
+
+// Valid, but missing BREAKS.
+switch ($something) {
+ case '1':
+ $case = '1';
+
+ case '2':
+ case '3':
+ $case = '5';
+
+ case '4':
+ $case = '4';
+
+ default:
+ $case = null;
+}
+
+// Invalid, and missing BREAKS.
+switch ($something) {
+ Case '1' :
+ $case = '1';
+
+case '2':
+ case '3' :
+ $case = '5';
+
+ case'4':
+ $case = '4';
+
+ Default :
+ $case = null;
+ $something = 'hello';
+ $other = 'hi';
+ }
+
+// Valid
+switch ($condition) {
+ case 'string':
+ $varStr = 'test';
+
+ default:
+ // Ignore the default.
+ break;
+}
+
+// No default comment
+switch ($condition) {
+ case 'string':
+ $varStr = 'test';
+
+ default:
+ break;
+}
+
+// Break problems
+switch ($condition) {
+ case 'string':
+
+
+ $varStr = 'test';
+
+ break;
+
+
+ case 'bool':
+ $varStr = 'test';
+
+
+ break;
+ default:
+
+ $varStr = 'test';
+ break;
+
+}
+
+switch ($var) {
+ case 'one':
+ case 'two':
+ break;
+
+ case 'three':
+ // Nothing to do.
+ break;
+
+ case 'four':
+ echo $hi;
+ break;
+
+ default:
+ // No default.
+ break;
+}
+
+switch ($var) {
+ case 'one':
+ if ($blah) {
+ }
+
+ break;
+
+ default:
+ // No default.
+ break;
+}
+
+switch ($name) {
+ case "1":
+ switch ($name2) {
+ case "1":
+ return true;
+ break;
+
+ case "2":
+ return true;
+ break;
+
+ default:
+ // No default.
+ break;
+ }
+ break;
+
+ case "2":
+switch ($name2) {
+ case "1":
+ return true;
+ break;
+
+ case "2":
+ return true;
+ break;
+
+ default:
+ // No default.
+ break;
+}
+ break;
+}
+
+switch ($name) {
+ case "1":
+ switch ($name2) {
+ case "1":
+ return true;
+
+ default:
+ // No default.
+ break;
+ }
+ break;
+
+ default:
+ // No default.
+ break;
+}
+
+switch ($name2) {
+ default:
+ // No default.
+ break;
+}
+
+switch ($foo) {
+ case "1":
+ return true;
+
+ default:
+ if ($foo === FALSE) {
+ break(2);
+ }
+ break;
+}
+
+// Valid SWITCH statement.
+switch ($something) {
+ case '1';
+ $case = '1';
+ return '1';
+
+ case '2';
+ case '3';
+ $case = '5';
+ return '2';
+
+ case '4';
+ $case = '4';
+ return '3';
+
+ default;
+ $case = null;
+ return '4';
+}
+
+switch ($something) {
+ case '1':
+ $case = '1';
+ break;
+
+ case '2':
+ throw new Exception('message');
+
+ default:
+ throw new Exception('message');
+}
+
+switch ($something) {
+ case '1';
+ echo 'one';
+ break;
+
+ default:
+ echo 'default';
+ exit;
+}
+
+switch ($foo) {
+ case '1':
+ return; // comment
+ break;
+
+}
+
+// Correct Multi line breaking statement with return.
+switch ($foo) {
+ case 1:
+ return array(
+ 'whiz',
+ 'bang',
+ );
+
+ case 2:
+ return helper_func(
+ 'whiz',
+ 'bang'
+ );
+
+ default:
+ throw new Exception();
+}
+
+switch ($foo) {
+ case 'bar':
+ throw new \Exception(
+ 'bar'
+ );
+
+ default:
+ throw new \Exception(
+ 'bar'
+ );
+}
--- /dev/null
+
+
+// Valid SWITCH statement.
+switch (something) {
+ case '1':
+ myvar = '1';
+ break;
+
+ case '2':
+ case '3':
+ myvar = '5';
+ break;
+
+ case '4':
+ myvar = '4';
+ break;
+
+ default:
+ myvar = null;
+ break;
+}
+
+// Alignment wrong.
+switch (something) {
+ case '1':
+ myvar = '1';
+ break;
+
+case '2':
+ case '3':
+ myvar = '5';
+ break;
+
+case '4':
+ myvar = '4';
+break;
+
+ default:
+ myvar = null;
+ break;
+}
+
+// Closing brace wrong.
+switch (something) {
+ case '1':
+ myvar = '1';
+ break;
+ }
+
+// PEAR style.
+switch (something) {
+case '1':
+ myvar = '1';
+ break;
+case '2':
+case '3':
+ myvar = '5';
+ break;
+case '4':
+ myvar = '4';
+ break;
+default:
+ myvar = null;
+ break;
+}
+
+// Valid, but missing BREAKS.
+switch (something) {
+ case '1':
+ myvar = '1';
+
+ case '2':
+ case '3':
+ myvar = '5';
+
+ case '4':
+ myvar = '4';
+
+ default:
+ myvar = null;
+}
+
+// Invalid, and missing BREAKS.
+switch (something) {
+ Case '1' :
+ myvar = '1';
+
+case '2':
+ case '3' :
+ myvar = '5';
+
+ case'4':
+ myvar = '4';
+
+ Default :
+ myvar = null;
+ something = 'hello';
+ other = 'hi';
+ }
+
+// Valid
+switch (condition) {
+ case 'string':
+ varStr = 'test';
+
+ default:
+ // Ignore the default.
+ break;
+}
+
+// No default comment
+switch (condition) {
+ case 'string':
+ varStr = 'test';
+
+ default:
+ break;
+}
+
+// Break problems
+switch (condition) {
+ case 'string':
+
+
+ varStr = 'test';
+
+ break;
+
+
+ case 'bool':
+ varStr = 'test';
+
+
+ break;
+ default:
+
+ varStr = 'test';
+ break;
+
+}
+
+switch (var) {
+ case 'one':
+ case 'two':
+ break;
+
+ case 'three':
+ // Nothing to do.
+ break;
+
+ case 'four':
+ echo hi;
+ break;
+
+ default:
+ // No default.
+ break;
+}
+
+switch (var) {
+ case 'one':
+ if (blah) {
+ }
+
+ break;
+
+ default:
+ // No default.
+ break;
+}
+
+switch (name) {
+ case "1":
+ switch (name2) {
+ case "1":
+ return true;
+ break;
+
+ case "2":
+ return true;
+ break;
+
+ default:
+ // No default.
+ break;
+ }
+ break;
+
+ case "2":
+switch (name2) {
+ case "1":
+ return true;
+ break;
+
+ case "2":
+ return true;
+ break;
+
+ default:
+ // No default.
+ break;
+}
+ break;
+}
+
+switch (name) {
+ case "1":
+ switch (name2) {
+ case "1":
+ return true;
+
+ default:
+ // No default.
+ break;
+ }
+ break;
+
+ default:
+ // No default.
+ break;
+}
+
+switch (name2) {
+ default:
+ // No default.
+ break;
+}
+
+switch (foo) {
+ case "1":
+ return true;
+
+ default:
+ if (foo === false) {
+ break;
+ }
+ break;
+}
+
+// Valid SWITCH statement.
+switch (something) {
+ case '1':
+ myvar = '1';
+ return '1';
+
+ case '2':
+ case '3':
+ myvar = '5';
+ return '2';
+
+ case '4':
+ myvar = '4';
+ return '3';
+
+ default:
+ myvar = null;
+ return '4';
+}
+
+switch (something) {
+ case '1':
+ myvar = '1';
+ break;
+
+ case '2':
+ throw 'message';
+
+ default:
+ throw 'message';
+}
+
+switch (something) {
+ case '1';
+ print('one');
+ break;
+
+ default:
+ print('default');
+ return;
+}
+
+switch (foo) {
+ case '1':
+ return; // comment
+ break;
+
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the SwitchDeclaration 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\Standards\Squiz\Tests\ControlStructures;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class SwitchDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='SwitchDeclarationUnitTest.inc')
+ {
+ return array(
+ 27 => 1,
+ 29 => 1,
+ 34 => 1,
+ 36 => 1,
+ 44 => 1,
+ 48 => 1,
+ 52 => 1,
+ 54 => 1,
+ 55 => 1,
+ 56 => 1,
+ 58 => 1,
+ 59 => 1,
+ 61 => 1,
+ 62 => 1,
+ 79 => 1,
+ 85 => 2,
+ 88 => 2,
+ 89 => 2,
+ 92 => 1,
+ 95 => 3,
+ 99 => 1,
+ 116 => 1,
+ 122 => 1,
+ 127 => 2,
+ 134 => 2,
+ 135 => 1,
+ 138 => 1,
+ 143 => 1,
+ 144 => 1,
+ 147 => 1,
+ 165 => 1,
+ 172 => 1,
+ 176 => 2,
+ 180 => 1,
+ 192 => 2,
+ 196 => 1,
+ 223 => 1,
+ 266 => 1,
+ 282 => 1,
+ 284 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='SwitchDeclarationUnitTest.inc')
+ {
+ if ($testFile === 'SwitchDeclarationUnitTest.js') {
+ return array(273 => 1);
+ }
+
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+alert('hi')
+alert('hi');
--- /dev/null
+<?php
+/**
+ * Unit test class for the JSLint 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\Standards\Squiz\Tests\Debug;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Config;
+
+class JSLintUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Should this test be skipped for some reason.
+ *
+ * @return void
+ */
+ protected function shouldSkipTest()
+ {
+ $jslPath = Config::getExecutablePath('jslint');
+ return (is_null($jslPath));
+
+ }//end shouldSkipTest()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 1 => 2,
+ 2 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+alert('hi')
+alert('hi');
--- /dev/null
+<?php
+/**
+ * Unit test class for the JavaScriptLint 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\Standards\Squiz\Tests\Debug;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Config;
+
+class JavaScriptLintUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Should this test be skipped for some reason.
+ *
+ * @return void
+ */
+ protected function shouldSkipTest()
+ {
+ $jslPath = Config::getExecutablePath('jsl');
+ return (is_null($jslPath));
+
+ }//end shouldSkipTest()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(2 => 1);
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+function myFunction() {}
+?>
--- /dev/null
+<?php
+class MyClass {}
+?>
--- /dev/null
+<?php
+interface MyInterface {}
+?>
--- /dev/null
+<?php
+trait MyTrait {}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the FileExtension 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\Standards\Squiz\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FileExtensionUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ switch ($testFile) {
+ case 'FileExtensionUnitTest.1.inc':
+ return array(1 => 1);
+ default:
+ return array();
+ }
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$value = ($one + $two);
+$value = $one + $two;
+
+$value = ($one - $two);
+$value = $one - $two;
+
+$value = ($one * $two);
+$value = $one * $two;
+
+$value = ($one / $two);
+$value = $one / $two;
+
+$value = ($one % $two);
+$value = $one % $two;
+
+$value = ($one + $two + $three);
+$value = $one + $two + $three;
+$value = ($one + ($two + $three));
+$value = $one + ($two + $three);
+
+$value++;
+$value--;
+$value = -1;
+$value = - 1;
+
+$value = (1 + 2);
+$value = 1 + 2;
+
+$value = (1 - 2);
+$value = 1 - 2;
+
+$value = (1 * 2);
+$value = 1 * 2;
+
+$value = (1 / 2);
+$value = 1 / 2;
+
+$value = (1 % 2);
+$value = 1 % 2;
+
+$value = (1 + 2 + 3);
+$value = 1 + 2 + 3;
+$value = (1 + (2 + 3));
+$value = 1 + (2 + 3);
+
+$value = $one + 2 + 3 - ($four * $five * (6 + 7)) + $nine + 2;
+$value = myFunction($tokens[$stackPtr - 1]);
+
+for ($i = 1 + 2; $i < 4 + 5; $i++) {
+}
+
+function myFunction()
+{
+ $value = ($one + 1) + ($two + 2) + (myFunction() + 2);
+ $value = myFunction() + 2;
+ $value = (myFunction($var) + myFunction2($var));
+ return -1;
+}
+
+$line = substr($line, 0, -2);
+$words = preg_split('|(\s+)|', $line."\n", -1, $flags);
+if (!isset($this->words[$wordPos-1]) || $this->words[$wordPos-1] !== ' ') {
+} else if ($this->tokens[$pos + 1] === "\n") {
+}
+
+if ($pos === count($this->tokens) - 1) {
+ $file = '...'.substr($file, (($padding * -1) + 3));
+}
+
+if (substr($basename, -5) !== 'Sniff') {
+ $i = ($this->_tokens[$i]['parenthesis_closer'] + 1);
+}
+
+$pos = $this->_getShortCommentEndPos();
+if ($pos === -1) {
+ $stackPtr = ($tokens[$next][$to] - 1);
+ $stackPtr = ($tokens[$next][$pattern[$i]['to']] + 1);
+ $var = (($var1 + $var2) + $var3 + $var4)
+}
+
+$commentStart = ($phpcsFile->findPrevious(T_DOC_COMMENT, ($commentEnd - 1), null, true) + 1);
+$commentEnd = ($this->_phpcsFile->findNext(T_DOC_COMMENT, ($commentStart + 1), null, true) - 1);
+$expected .= '...'.substr($tokens[($stackPtr - 2)]['content'], -5).$tokens[$stackPtr]['content'];
+
+if (($tokens[$nextToken - 1]['code']) !== T_WHITESPACE) {
+ $errorPos = ($params[(count($params) - 1)]->getLine() + $commentStart);
+}
+
+while (($nextSpace = $phpcsFile->findNext(T_WHITESPACE, $nextSpace + 1, $nextBreak)) !== false) {
+}
+
+foreach ($attributes as $id => &$attribute) {
+}
+
+class MyClass
+{
+
+
+ public function &myFunction(array &$one, array &$two)
+ {
+
+ }//end myFunction()
+
+
+}//end class
+
+if ($index < -1) $index = 0;
+if ($index < - 1) $index = 0;
+
+$three = ceil($one / $two);
+$three = ceil(($one / $two) / $four);
+$three = ceil($one / ($two / $four));
+
+$four = -0.25;
+
+$three = ceil($one[1] / $two);
+
+switch ($number % 10) {
+ case -1:
+ $suffix = 'st';
+ break;
+}
+
+$expectedPermission = array(
+ 'granted' => 4,
+ 'denied' => 1,
+ 'cascade' => TRUE,
+ 'blockergranted' => 2,
+ 'blockerdenied' => - 3,
+ 'effective' => TRUE,
+ );
+
+$value = (int) isset($blah) + 2;
+$value = (int) isset($blah) + (int) isset($foo) + (int) isset($bar);
+
+doSomething(getValue($var, 2)) - $y;
+
+$codeFiles = array($global => $codeFiles[$global]) + $codeFiles;
+
+$var = array(-1);
+$var = [-1];
+$var = [0, -1, -2];
+
+$cntPages = ceil(count($items) / self::ON_PAGE);
+
+error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
+error_reporting(E_ALL & ~E_NOTICE | ~E_WARNING);
+$results = $stmt->fetchAll(\PDO::FETCH_ASSOC | \PDO::FETCH_GROUP | \PDO::FETCH_UNIQUE);
+$di = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS);
+foo(1 + 2 + 3);
+
+if (empty($foo[-1]) === true) {
+ $foo[-1] = 'foo';
+}
+
+try {
+} catch (AException | BException $e) {
+}
+
+$var = $foo['blah'] + [];
--- /dev/null
+<?php
+$value = ($one + $two);
+$value = ($one + $two);
+
+$value = ($one - $two);
+$value = ($one - $two);
+
+$value = ($one * $two);
+$value = ($one * $two);
+
+$value = ($one / $two);
+$value = ($one / $two);
+
+$value = ($one % $two);
+$value = ($one % $two);
+
+$value = ($one + $two + $three);
+$value = ($one + $two + $three);
+$value = ($one + ($two + $three));
+$value = ($one + ($two + $three));
+
+$value++;
+$value--;
+$value = -1;
+$value = - 1;
+
+$value = (1 + 2);
+$value = (1 + 2);
+
+$value = (1 - 2);
+$value = (1 - 2);
+
+$value = (1 * 2);
+$value = (1 * 2);
+
+$value = (1 / 2);
+$value = (1 / 2);
+
+$value = (1 % 2);
+$value = (1 % 2);
+
+$value = (1 + 2 + 3);
+$value = (1 + 2 + 3);
+$value = (1 + (2 + 3));
+$value = (1 + (2 + 3));
+
+$value = ($one + 2 + 3 - ($four * $five * (6 + 7)) + $nine + 2);
+$value = myFunction($tokens[($stackPtr - 1)]);
+
+for ($i = (1 + 2); $i < (4 + 5); $i++) {
+}
+
+function myFunction()
+{
+ $value = (($one + 1) + ($two + 2) + (myFunction() + 2));
+ $value = (myFunction() + 2);
+ $value = (myFunction($var) + myFunction2($var));
+ return -1;
+}
+
+$line = substr($line, 0, -2);
+$words = preg_split('|(\s+)|', $line."\n", -1, $flags);
+if (!isset($this->words[($wordPos-1)]) || $this->words[($wordPos-1)] !== ' ') {
+} else if ($this->tokens[($pos + 1)] === "\n") {
+}
+
+if ($pos === (count($this->tokens) - 1)) {
+ $file = '...'.substr($file, (($padding * -1) + 3));
+}
+
+if (substr($basename, -5) !== 'Sniff') {
+ $i = ($this->_tokens[$i]['parenthesis_closer'] + 1);
+}
+
+$pos = $this->_getShortCommentEndPos();
+if ($pos === -1) {
+ $stackPtr = ($tokens[$next][$to] - 1);
+ $stackPtr = ($tokens[$next][$pattern[$i]['to']] + 1);
+ $var = (($var1 + $var2) + $var3 + $var4)
+}
+
+$commentStart = ($phpcsFile->findPrevious(T_DOC_COMMENT, ($commentEnd - 1), null, true) + 1);
+$commentEnd = ($this->_phpcsFile->findNext(T_DOC_COMMENT, ($commentStart + 1), null, true) - 1);
+$expected .= '...'.substr($tokens[($stackPtr - 2)]['content'], -5).$tokens[$stackPtr]['content'];
+
+if (($tokens[($nextToken - 1)]['code']) !== T_WHITESPACE) {
+ $errorPos = ($params[(count($params) - 1)]->getLine() + $commentStart);
+}
+
+while (($nextSpace = $phpcsFile->findNext(T_WHITESPACE, ($nextSpace + 1), $nextBreak)) !== false) {
+}
+
+foreach ($attributes as $id => &$attribute) {
+}
+
+class MyClass
+{
+
+
+ public function &myFunction(array &$one, array &$two)
+ {
+
+ }//end myFunction()
+
+
+}//end class
+
+if ($index < -1) $index = 0;
+if ($index < - 1) $index = 0;
+
+$three = ceil($one / $two);
+$three = ceil(($one / $two) / $four);
+$three = ceil($one / ($two / $four));
+
+$four = -0.25;
+
+$three = ceil($one[1] / $two);
+
+switch ($number % 10) {
+ case -1:
+ $suffix = 'st';
+ break;
+}
+
+$expectedPermission = array(
+ 'granted' => 4,
+ 'denied' => 1,
+ 'cascade' => TRUE,
+ 'blockergranted' => 2,
+ 'blockerdenied' => - 3,
+ 'effective' => TRUE,
+ );
+
+$value = ((int) isset($blah) + 2);
+$value = ((int) isset($blah) + (int) isset($foo) + (int) isset($bar));
+
+(doSomething(getValue($var, 2)) - $y);
+
+$codeFiles = (array($global => $codeFiles[$global]) + $codeFiles);
+
+$var = array(-1);
+$var = [-1];
+$var = [0, -1, -2];
+
+$cntPages = ceil(count($items) / self::ON_PAGE);
+
+error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
+error_reporting(E_ALL & ~E_NOTICE | ~E_WARNING);
+$results = $stmt->fetchAll(\PDO::FETCH_ASSOC | \PDO::FETCH_GROUP | \PDO::FETCH_UNIQUE);
+$di = new \RecursiveDirectoryIterator($path, (\RecursiveDirectoryIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS));
+foo(1 + 2 + 3);
+
+if (empty($foo[-1]) === true) {
+ $foo[-1] = 'foo';
+}
+
+try {
+} catch (AException | BException $e) {
+}
+
+$var = ($foo['blah'] + []);
--- /dev/null
+value = (one + two);
+value = one + two;
+
+value = (one - two);
+value = one - two;
+
+value = (one * two);
+value = one * two;
+
+value = (one / two);
+value = one / two;
+
+value = (one % two);
+value = one % two;
+
+value = (one + two + three);
+value = one + two + three;
+value = (one + (two + three));
+value = one + (two + three);
+
+value++;
+value--;
+value = -1;
+value = - 1;
+
+value = (1 + 2);
+value = 1 + 2;
+
+value = (1 - 2);
+value = 1 - 2;
+
+value = (1 * 2);
+value = 1 * 2;
+
+value = (1 / 2);
+value = 1 / 2;
+
+value = (1 % 2);
+value = 1 % 2;
+
+value = (1 + 2 + 3);
+value = 1 + 2 + 3;
+value = (1 + (2 + 3));
+value = 1 + (2 + 3);
+
+value = one + 2 + 3 - (four * five * (6 + 7)) + nine + 2;
+value = myFunction(tokens[stackPtr - 1]);
+
+for (i = 1 + 2; i < 4 + 5; i++) {
+}
+
+function myFunction()
+{
+ value = (one + 1) + (two + 2) + (myFunction() + 2);
+ value = myFunction() + 2;
+ value = (myFunction(mvar) + myFunction2(mvar));
+ return -1;
+}
+
+params['mode'] = id.replace(/WidgetType/, '');
+
+if (index < -1) index = 0;
+if (index < - 1) index = 0;
+
+var classN = prvId.replace(/\./g, '-');
+
+three = myFunction(one / two);
+three = myFunction((one / two) / four);
+three = myFunction(one / (two / four));
+
+four = -0.25;
+
+id = id.replace(/row\/:/gi, '');
+return /MSIE/.test(navigator.userAgent);
+
+var re = new RegExp(/<\/?(\w+)((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/gim);
+
+var options = {
+ minVal: -1,
+ maxVal: -1
+};
+
+stepWidth = Math.round(this.width / 5);
+
+date.setMonth(d[2] - 1);
+
+switch (number % 10) {
+ case -1:
+ suffix = 'st';
+ break;
+}
+
+var pathSplit = ipt.value.split(/\/|\\/);
+
+if (pairs[i].search(/=/) !== -1) {
+}
+
+if (urlValue.search(/[a-zA-z]+:\/\//) !== 0) {
+}
+
+if (urlValue.search(/[a-zA-z]+:\/\/*/) !== 0) {
+}
+
+if (!value || /^\s*$/.test(value)) {
+ return true;
+}
+
+parseInt(dfx.attr(selectors[idx], 'elemOffsetTop'), 10) - scrollCoords.y + 'px'
+
+if (something === true
+ ^ somethingElse === true
+) {
+ return false;
+}
+
+if (true === /^\d*\.?\d*$/.test(input)) return true;
--- /dev/null
+value = (one + two);
+value = one + two;
+
+value = (one - two);
+value = (one - two);
+
+value = (one * two);
+value = (one * two);
+
+value = (one / two);
+value = (one / two);
+
+value = (one % two);
+value = (one % two);
+
+value = (one + two + three);
+value = one + two + three;
+value = (one + (two + three));
+value = one + (two + three);
+
+value++;
+value--;
+value = -1;
+value = - 1;
+
+value = (1 + 2);
+value = 1 + 2;
+
+value = (1 - 2);
+value = (1 - 2);
+
+value = (1 * 2);
+value = (1 * 2);
+
+value = (1 / 2);
+value = (1 / 2);
+
+value = (1 % 2);
+value = (1 % 2);
+
+value = (1 + 2 + 3);
+value = 1 + 2 + 3;
+value = (1 + (2 + 3));
+value = 1 + (2 + 3);
+
+value = one + 2 + (3 - (four * five * (6 + 7))) + nine + 2;
+value = myFunction(tokens[(stackPtr - 1)]);
+
+for (i = 1 + 2; i < 4 + 5; i++) {
+}
+
+function myFunction()
+{
+ value = (one + 1) + (two + 2) + (myFunction() + 2);
+ value = myFunction() + 2;
+ value = (myFunction(mvar) + myFunction2(mvar));
+ return -1;
+}
+
+params['mode'] = id.replace(/WidgetType/, '');
+
+if (index < -1) index = 0;
+if (index < - 1) index = 0;
+
+var classN = prvId.replace(/\./g, '-');
+
+three = myFunction(one / two);
+three = myFunction((one / two) / four);
+three = myFunction(one / (two / four));
+
+four = -0.25;
+
+id = id.replace(/row\/:/gi, '');
+return /MSIE/.test(navigator.userAgent);
+
+var re = new RegExp(/<\/?(\w+)((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/gim);
+
+var options = {
+ minVal: -1,
+ maxVal: -1
+};
+
+stepWidth = Math.round(this.width / 5);
+
+date.setMonth(d[2] - 1);
+
+switch (number % 10) {
+ case -1:
+ suffix = 'st';
+ break;
+}
+
+var pathSplit = ipt.value.split(/\/|\\/);
+
+if (pairs[i].search(/=/) !== -1) {
+}
+
+if (urlValue.search(/[a-zA-z]+:\/\//) !== 0) {
+}
+
+if (urlValue.search(/[a-zA-z]+:\/\/*/) !== 0) {
+}
+
+if (!value || /^\s*$/.test(value)) {
+ return true;
+}
+
+(parseInt(dfx.attr(selectors[idx], 'elemOffsetTop'), 10) - scrollCoords.y) + 'px'
+
+if (something === true
+ ^ somethingElse === true
+) {
+ return false;
+}
+
+if (true === /^\d*\.?\d*$/.test(input)) return true;
--- /dev/null
+<?php
+/**
+ * Unit test class for the OperatorBracket 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\Standards\Squiz\Tests\Formatting;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class OperatorBracketUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='OperatorBracketUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'OperatorBracketUnitTest.inc':
+ return array(
+ 3 => 1,
+ 6 => 1,
+ 9 => 1,
+ 12 => 1,
+ 15 => 1,
+ 18 => 2,
+ 20 => 1,
+ 25 => 1,
+ 28 => 1,
+ 31 => 1,
+ 34 => 1,
+ 37 => 1,
+ 40 => 1,
+ 43 => 2,
+ 45 => 1,
+ 47 => 5,
+ 48 => 1,
+ 50 => 2,
+ 55 => 2,
+ 56 => 1,
+ 63 => 2,
+ 64 => 1,
+ 67 => 1,
+ 86 => 1,
+ 90 => 1,
+ 109 => 1,
+ 130 => 1,
+ 134 => 1,
+ 135 => 2,
+ 137 => 1,
+ 139 => 1,
+ 150 => 1,
+ 161 => 1,
+ );
+ break;
+ case 'OperatorBracketUnitTest.js':
+ return array(
+ 5 => 1,
+ 8 => 1,
+ 11 => 1,
+ 14 => 1,
+ 24 => 1,
+ 30 => 1,
+ 33 => 1,
+ 36 => 1,
+ 39 => 1,
+ 46 => 1,
+ 47 => 1,
+ 63 => 1,
+ 108 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+function myFunc() {}
+function myFunc( ) {}
+function myFunc($blah) {}
+function myFunc( $blah ) {}
+function myFunc($blah, $blah2, $blah3) {}
+function myFunc($blah , $blah2, $blah3 ) {}
+function myFunc($blah,$blah2,$blah3) {}
+function myFunc($blah, $blah2, $blah3) {}
+function myFunc($blah='hello') {}
+function myFunc($blah = 'hello') {}
+function myFunc(PHP_CodeSniffer $object, array $array=array(), $blah3='yo') {}
+function myFunc( $blah='hello' , $blah2= 'hi', $blah3 = 'yo' ) {}
+function myFunc(PHP_CodeSniffer $object, array $array=array(), $blah3='yo') {}
+function myFunc( PHP_CodeSniffer $object, array $array=array(), $blah3='yo') {}
+function myFunc( array &$one, array &$two) {}
+function myFunc(&$blah) {}
+function myFunc( &$blah ) {}
+
+function multiLineFunction(
+ array $flatList,
+ $markup,
+ array $otherList,
+ $lastOffset=0
+) {
+}
+
+function multiLineFunction(
+ $markup,
+ array $otherList,
+ $lastOffset=0
+) {
+}
+
+$noArgs_longVars = function ($longVar1, $longerVar2= false) use (
+ $longVar1 , $longerVar2= false,
+ $muchLongerVar3
+) {
+ // body
+};
+
+// @codingStandardsChangeSetting Squiz.Functions.FunctionDeclarationArgumentSpacing equalsSpacing 1
+function myFunc($blah = 'hello') {}
+function myFunc($blah = 'hello') {}
+function myFunc($blah = 'hello') {}
+function myFunc($blah = 'hello') {}
+// @codingStandardsChangeSetting Squiz.Functions.FunctionDeclarationArgumentSpacing equalsSpacing 0
+
+// @codingStandardsChangeSetting Squiz.Functions.FunctionDeclarationArgumentSpacing requiredSpacesAfterOpen 1
+// @codingStandardsChangeSetting Squiz.Functions.FunctionDeclarationArgumentSpacing requiredSpacesBeforeClose 1
+function myFunc($blah) {}
+function myFunc( $blah ) {}
+function myFunc( $blah ) {}
+function myFunc( array $blah ) {}
+function myFunc(array $blah ) {}
+function myFunc( array $blah ) {}
+function myFunc() {}
+function myFunc( ) {}
+
+function multiLineFunction(
+ array $flatList,
+ $markup,
+ array $otherList,
+ $lastOffset=0
+) {
+}
+// @codingStandardsChangeSetting Squiz.Functions.FunctionDeclarationArgumentSpacing requiredSpacesAfterOpen 0
+// @codingStandardsChangeSetting Squiz.Functions.FunctionDeclarationArgumentSpacing requiredSpacesBeforeClose 0
+
+function myFunc($req, $opt=null, ...$params) {}
+function myFunc($param, &...$moreParams) {}
+
+function MissingParamTypeInDocBlock(array$a = null, callable$c, \ArrayObject$o, $foo = []) {}
+
+function myFunc(...$args) {}
+function myFunc( ...$args) {}
+function myFunc(... $args) {}
--- /dev/null
+<?php
+function myFunc() {}
+function myFunc() {}
+function myFunc($blah) {}
+function myFunc($blah) {}
+function myFunc($blah, $blah2, $blah3) {}
+function myFunc($blah, $blah2, $blah3) {}
+function myFunc($blah, $blah2, $blah3) {}
+function myFunc($blah, $blah2, $blah3) {}
+function myFunc($blah='hello') {}
+function myFunc($blah='hello') {}
+function myFunc(PHP_CodeSniffer $object, array $array=array(), $blah3='yo') {}
+function myFunc($blah='hello', $blah2='hi', $blah3='yo') {}
+function myFunc(PHP_CodeSniffer $object, array $array=array(), $blah3='yo') {}
+function myFunc(PHP_CodeSniffer $object, array $array=array(), $blah3='yo') {}
+function myFunc(array &$one, array &$two) {}
+function myFunc(&$blah) {}
+function myFunc(&$blah) {}
+
+function multiLineFunction(
+ array $flatList,
+ $markup,
+ array $otherList,
+ $lastOffset=0
+) {
+}
+
+function multiLineFunction(
+ $markup,
+ array $otherList,
+ $lastOffset=0
+) {
+}
+
+$noArgs_longVars = function ($longVar1, $longerVar2=false) use (
+ $longVar1, $longerVar2=false,
+ $muchLongerVar3
+) {
+ // body
+};
+
+// @codingStandardsChangeSetting Squiz.Functions.FunctionDeclarationArgumentSpacing equalsSpacing 1
+function myFunc($blah = 'hello') {}
+function myFunc($blah = 'hello') {}
+function myFunc($blah = 'hello') {}
+function myFunc($blah = 'hello') {}
+// @codingStandardsChangeSetting Squiz.Functions.FunctionDeclarationArgumentSpacing equalsSpacing 0
+
+// @codingStandardsChangeSetting Squiz.Functions.FunctionDeclarationArgumentSpacing requiredSpacesAfterOpen 1
+// @codingStandardsChangeSetting Squiz.Functions.FunctionDeclarationArgumentSpacing requiredSpacesBeforeClose 1
+function myFunc( $blah ) {}
+function myFunc( $blah ) {}
+function myFunc( $blah ) {}
+function myFunc( array $blah ) {}
+function myFunc( array $blah ) {}
+function myFunc( array $blah ) {}
+function myFunc() {}
+function myFunc() {}
+
+function multiLineFunction(
+ array $flatList,
+ $markup,
+ array $otherList,
+ $lastOffset=0
+) {
+}
+// @codingStandardsChangeSetting Squiz.Functions.FunctionDeclarationArgumentSpacing requiredSpacesAfterOpen 0
+// @codingStandardsChangeSetting Squiz.Functions.FunctionDeclarationArgumentSpacing requiredSpacesBeforeClose 0
+
+function myFunc($req, $opt=null, ...$params) {}
+function myFunc($param, &...$moreParams) {}
+
+function MissingParamTypeInDocBlock(array $a=null, callable $c, \ArrayObject $o, $foo=[]) {}
+
+function myFunc(...$args) {}
+function myFunc(...$args) {}
+function myFunc(... $args) {}
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionDeclarationArgumentSpacing 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\Standards\Squiz\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionDeclarationArgumentSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 5 => 2,
+ 7 => 2,
+ 8 => 2,
+ 9 => 2,
+ 11 => 2,
+ 13 => 7,
+ 14 => 2,
+ 15 => 2,
+ 16 => 4,
+ 18 => 2,
+ 35 => 2,
+ 36 => 3,
+ 44 => 2,
+ 45 => 1,
+ 46 => 1,
+ 51 => 2,
+ 53 => 2,
+ 55 => 1,
+ 56 => 1,
+ 58 => 1,
+ 73 => 7,
+ 76 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+
+function func1()
+{
+
+}//end func1()
+
+
+function func1() {
+
+}//end func1()
+
+
+class MyClass
+{
+
+
+ private function func1()
+ {
+
+ }//end func1()
+
+
+ public function func1() {
+
+ }//end func1()
+
+
+}//end class
+
+
+abstract class MyClass
+{
+
+
+ abstract function func1();
+
+
+ public function func1() {
+
+ }//end func1()
+
+
+}//end class
+
+
+interface MyInterface
+{
+
+
+ function func1();
+
+
+ function func2() ;
+
+
+}//end interface
+
+function recurseCreateTree(
+ array $flatList,
+ $markup,
+ $lastOffset=0
+) {
+}
+
+
+function test ( ){}
+
+function parent() {}
+function self() {}
+function false() {}
+function true() {}
+function null() {}
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionDeclaration 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\Standards\Squiz\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 55 => 1,
+ 68 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+function myFunc($blah, $x, $blah) {}
+function myFunc($a, $b, $c) {}
+function myFunc($a, $a, $a) {}
+function myFunc($a='1', $b='1', $a='2') {}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionDuplicateArgument 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\Standards\Squiz\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionDuplicateArgumentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 4 => 2,
+ 5 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+function func1() {}
+
+class MyClass
+{
+ function func1() {}
+}
+
+interface MyInterface
+{
+ function func1() {}
+}
+
+function __autoload($class) {}
+
+echo preg_replace_callback('~-([a-z])~', function ($match) { return strtoupper($match[1]); }, 'hello-world');
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the GlobalFunction 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\Standards\Squiz\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class GlobalFunctionUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(2 => 1);
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+Function MyFunction() {}
+Public function MyFunction() {}
+Private function MyFunction() {}
+Protected function MyFunction() {}
+Static function MyFunction() {}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the LowercaseFunctionKeywords 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\Standards\Squiz\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LowercaseFunctionKeywordsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 3 => 1,
+ 4 => 1,
+ 5 => 1,
+ 6 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+public function someFunctionWithAVeryLongName($firstParameter='something',
+ $secondParameter='booooo',
+ $third=null, $fourthParameter=false,
+ $fifthParameter=123.12,
+ $sixthParam=true
+){
+}
+
+function someFunctionWithAVeryLongName2(
+$firstParameter='something',
+$secondParameter='booooo',
+) {
+}
+
+function blah() {
+}
+
+function blah()
+{
+}
+
+class MyClass
+{
+
+ public function someFunctionWithAVeryLongName(
+ $firstParameter='something',
+ $secondParameter='booooo',
+ $third=null,
+ $fourthParameter=false,
+ $fifthParameter=123.12,
+ $sixthParam=true
+ ) /** w00t */ {
+ }
+
+ public function someFunctionWithAVeryLongName2($firstParameter='something',
+ $secondParameter='booooo',
+ $third=null
+ ) {
+ }
+
+ public function someFunctionWithAVeryLongName3(
+ $firstParameter, $secondParameter, $third=null
+ ) {
+ }
+
+ public function someFunctionWithAVeryLongName4(
+ $firstParameter, $secondParameter
+ ) {
+ }
+
+ public function someFunctionWithAVeryLongName5(
+ $firstParameter,
+ $secondParameter=array(1,2,3),
+ $third=null
+ ) {
+ }
+
+}
+
+$noArgs_longVars = function () use (
+ $longVar1,
+ $longerVar2,
+ $muchLongerVar3
+) {
+ // body
+};
+
+$longArgs_longVars = function (
+ $longArgument,
+ $longerArgument,
+ $muchLongerArgument
+) use (
+ $longVar1,
+ $longerVar2,
+ $muchLongerVar3
+) {
+ // body
+};
+
+$longArgs_longVars = function ($longArgument,
+ $muchLongerArgument) use ($muchLongerVar3
+) {
+ // body
+};
+
+$noArgs_longVars = function () use (
+ $longVar1, $longerVar2,
+ $muchLongerVar3
+) {
+ // body
+};
+
+usort(
+ $data,
+ function ($a, $b) {
+ // body
+ }
+);
+
+function myFunction(
+ $firstParameter,$secondParameter=[1,2,3],$third=null
+) {
+}
+
+if (array_filter(
+ $commands,
+ function ($cmd) use ($commandName) {
+ return ($cmd['name'] == $commandName);
+ }
+)) {
+ // Do something
+}
+
+function foo( // comment
+ $bar,
+ $baz
+) { // comment
+ // ...
+}
+
+function foo($bar = [
+ 1,
+ 2,
+], $foo)
+{
+ // body
+}
+
+$foo = function ($bar = [
+ 1,
+ 2,
+]) use ($longVar1, $longerVar2) {
+ // body
+};
+
+function foo($bar = [
+ 1,
+ 2,
+],
+$foo)
+{
+ // body
+}
+
+function foo(
+ $bar = [
+ 1,
+ 2,
+ ],
+ $foo
+) {
+ // body
+}
+
+function foo(
+ $param1,
+
+ $param2,
+
+ $param3,
+) : SomeClass {
+}
--- /dev/null
+
+function someFunctionWithAVeryLongName(firstParameter='something',
+ secondParameter='booooo',
+ third=null, fourthParameter=false,
+ fifthParameter=123.12,
+ sixthParam=true
+){
+}
+
+function someFunctionWithAVeryLongName2(
+firstParameter='something',
+secondParameter='booooo',
+) {
+}
+
+function blah() {
+}
+
+function blah()
+{
+}
+
+var object =
+{
+
+ someFunctionWithAVeryLongName: function(
+ firstParameter='something',
+ secondParameter='booooo',
+ third=null,
+ fourthParameter=false,
+ fifthParameter=123.12,
+ sixthParam=true
+ ) /** w00t */ {
+ }
+
+ someFunctionWithAVeryLongName2: function (firstParameter='something',
+ secondParameter='booooo',
+ third=null
+ ) {
+ }
+
+ someFunctionWithAVeryLongName3: function (
+ firstParameter, secondParameter, third=null
+ ) {
+ }
+
+ someFunctionWithAVeryLongName4: function (
+ firstParameter, secondParameter
+ ) {
+ }
+
+ someFunctionWithAVeryLongName5: function (
+ firstParameter,
+ secondParameter=array(1,2,3),
+ third=null
+ ) {
+ }
+
+}
+
+var a = Function('return 1+1');
+
+class test
+{
+ myFunction() {
+ return false;
+ }
+
+ myFunction2()
+ {
+ return false;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the MultiLineFunctionDeclaration 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\Standards\Squiz\Tests\Functions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class MultiLineFunctionDeclarationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='MultiLineFunctionDeclarationUnitTest.inc')
+ {
+ if ($testFile === 'MultiLineFunctionDeclarationUnitTest.inc') {
+ $errors = array(
+ 2 => 1,
+ 3 => 1,
+ 4 => 2,
+ 5 => 1,
+ 7 => 1,
+ 11 => 1,
+ 12 => 1,
+ 13 => 1,
+ 16 => 1,
+ 36 => 1,
+ 43 => 2,
+ 48 => 1,
+ 81 => 1,
+ 82 => 2,
+ 88 => 1,
+ 102 => 2,
+ 137 => 1,
+ 141 => 2,
+ 142 => 1,
+ 158 => 1,
+ 160 => 1,
+ );
+ } else {
+ $errors = array(
+ 2 => 1,
+ 3 => 1,
+ 4 => 2,
+ 5 => 1,
+ 7 => 1,
+ 11 => 1,
+ 12 => 1,
+ 13 => 1,
+ 16 => 1,
+ 26 => 1,
+ 36 => 1,
+ 43 => 2,
+ 48 => 1,
+ 65 => 1,
+ );
+ }//end if
+
+ return $errors;
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+function getSomeValue() {}
+function parseMyDSN() {}
+function get_some_value() {}
+function getSomeValue_Again() {}
+function _getSomeValue() {}
+function _parseMyDSN() {}
+function _get_some_value() {}
+function _getSomeValue_Again() {}
+
+function __construct() {}
+function __destruct() {}
+function __myFunction() {}
+function __my_function() {}
+
+function XMLParser() {}
+function xmlParser() {}
+
+echo preg_replace_callback('~-([a-z])~', function ($match) { return strtoupper($match[1]); }, 'hello-world');
+
+/* @codingStandardsIgnoreStart */
+class MyClass
+{
+ /* @codingStandardsIgnoreEnd */
+ public function __construct() {}
+}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the ValidFunctionName 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\Standards\Squiz\Tests\NamingConventions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ValidFunctionNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 4 => 1,
+ 5 => 1,
+ 6 => 1,
+ 7 => 1,
+ 8 => 1,
+ 9 => 1,
+ 11 => 1,
+ 12 => 1,
+ 13 => 1,
+ 14 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$varName = 'hello';
+$var_name = 'hello';
+$varname = 'hello';
+$_varName = 'hello';
+
+class MyClass
+{
+ $varName = 'hello';
+ $var_name = 'hello';
+ $varname = 'hello';
+ $_varName = 'hello';
+
+ public $varName = 'hello';
+ public $var_name = 'hello';
+ public $varname = 'hello';
+ public $_varName = 'hello';
+
+ protected $varName = 'hello';
+ protected $var_name = 'hello';
+ protected $varname = 'hello';
+ protected $_varName = 'hello';
+
+ private $_varName = 'hello';
+ private $_var_name = 'hello';
+ private $_varname = 'hello';
+ private $varName = 'hello';
+}
+
+echo $varName;
+echo $var_name;
+echo $varname;
+echo $_varName;
+
+echo "Hello $varName";
+echo "Hello $var_name";
+echo "Hello ${var_name}";
+echo "Hello $varname";
+echo "Hello $_varName";
+
+echo 'Hello '.$varName;
+echo 'Hello '.$var_name;
+echo 'Hello '.$varname;
+echo 'Hello '.$_varName;
+
+echo $_SERVER['var_name'];
+echo $_REQUEST['var_name'];
+echo $_GET['var_name'];
+echo $_POST['var_name'];
+echo $GLOBALS['var_name'];
+
+echo MyClass::$varName;
+echo MyClass::$var_name;
+echo MyClass::$varname;
+echo MyClass::$_varName;
+
+echo $this->varName2;
+echo $this->var_name2;
+echo $this->varname2;
+echo $this->_varName2;
+echo $object->varName2;
+echo $object->var_name2;
+echo $object_name->varname2;
+echo $object_name->_varName2;
+
+echo $this->myFunction($one, $two);
+echo $object->myFunction($one_two);
+
+$error = "format is \$GLOBALS['$varName']";
+
+echo $_SESSION['var_name'];
+echo $_FILES['var_name'];
+echo $_ENV['var_name'];
+echo $_COOKIE['var_name'];
+
+$XML = 'hello';
+$myXML = 'hello';
+$XMLParser = 'hello';
+$xmlParser = 'hello';
+
+echo "{$_SERVER['HOSTNAME']} $var_name";
+
+// Need to be the last thing in this test file.
+$obj->$classVar = $prefix.'-'.$type;
+
+class foo
+{
+ const bar = <<<BAZ
+qux
+BAZ;
+}
+
+$foo = <<<'BAR'
+$123
+"$456"
+BAR;
+
+$foo = <<<BAR
+$123
+"$456"
+BAR;
+
+class a
+{
+ protected
+ $_sheet,
+ $_FieldParser,
+ $_key;
+}
+
+var_dump($http_response_header);
+var_dump($HTTP_RAW_POST_DATA);
+var_dump($php_errormsg);
+
+trait MyTrait
+{
+ public $_varName = 'hello';
+ private $_varName = 'hello';
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ValidVariableName 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\Standards\Squiz\Tests\NamingConventions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ValidVariableNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ $errors = array(
+ 3 => 1,
+ 5 => 1,
+ 10 => 1,
+ 12 => 1,
+ 15 => 1,
+ 17 => 1,
+ 20 => 1,
+ 22 => 1,
+ 25 => 1,
+ 27 => 1,
+ 31 => 1,
+ 33 => 1,
+ 36 => 1,
+ 37 => 1,
+ 39 => 1,
+ 42 => 1,
+ 44 => 1,
+ 53 => 1,
+ 58 => 1,
+ 62 => 1,
+ 63 => 1,
+ 64 => 1,
+ 67 => 1,
+ 81 => 1,
+ 106 => 1,
+ 107 => 1,
+ 108 => 1,
+ 117 => 1,
+ );
+
+ return $errors;
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+function test(id)
+{
+
+ this.id = id;
+
+}
+/**/
+test.prototype = {
+ init: function()
+ {
+ var x = {};
+ x.name = 'test';
+ x['phone'] = 123124324;
+ var t = ['test', 'this'].join('');
+ var y = ['test'].join('');
+ var a = x[0];
+ var z = x[x['name']];
+ var p = x[x.name];
+ }
+
+};
+
+function test() {
+ this.errors['step_' + step] = errors;
+ this.errors['test'] = x;
+ this.errors['test' + 10] = x;
+ this.errors['test' + y] = x;
+ this.errors['test' + 'blah'] = x;
+ this.errors[y] = x;
+ this.errors[y + z] = x;
+ this.permissions['workflow.cancel'] = x;
+}
+
+if (child.prototype) {
+ above.prototype['constructor'] = parent;
+ child.prototype['super'] = new above();
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowObjectStringIndex sniff.
+ *
+ * @author Sertan Danis <sdanis@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\Standards\Squiz\Tests\Objects;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowObjectStringIndexUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='DisallowObjectStringIndexUnitTest.js')
+ {
+ if ($testFile !== 'DisallowObjectStringIndexUnitTest.js') {
+ return array();
+ }
+
+ return array(
+ 13 => 1,
+ 17 => 1,
+ 25 => 1,
+ 35 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$obj = new MyClass();
+$obj =& new MyClass();
+$obj = &new MyClass();
+new MyClass();
+
+$objects = array('one' => new MyClass());
+$object->myFunction(new MyClass());
+
+throw new MyException($msg);
+
+function foo() { return new MyClass(); }
+
+$doodad = $x ? new Foo : new Bar;
+
+function new
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the ObjectInstantiation 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\Standards\Squiz\Tests\Objects;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ObjectInstantiationUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 5 => 1,
+ 8 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+this.request({ action: 'getTypeFormatContents', });
+
+addTypeFormatButton.addClickEvent(function() {
+ self.addNewTypeFormat();
+});
+
+var x = {};
+
+var y = {
+ VarOne : 'If you ask me, thats if you ask',
+ VarTwo : ['Alonzo played you', 'for a fool', 'esse'],
+ VarThree: function(arg) {
+ console.info(1);
+ }
+};
+
+var z = {
+ VarOne : 'If you ask me, thats if you ask',
+ VarTwo : ['Alonzo played you', 'for a fool', 'esse'],
+ VarThree: function(arg) {
+ console.info(1);
+ },
+};
+
+var x = function() {
+ console.info(2);
+};
+
+AssetListingEditWidgetType.prototype = {
+ init: function(data, assetid, editables)
+ {
+ }
+};
+
+AssetListingEditWidgetType.prototype = {
+ init: function(data, assetid, editables)
+ {
+ },
+};
--- /dev/null
+<?php
+/**
+ * Unit test class for the ObjectMemberComma 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\Standards\Squiz\Tests\Objects;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ObjectMemberCommaUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='ObjectMemberCommaUnitTest.js')
+ {
+ if ($testFile !== 'ObjectMemberCommaUnitTest.js') {
+ return array();
+ }
+
+ return array(
+ 1 => 1,
+ 22 => 1,
+ 38 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+if ($value === TRUE) {
+} else if ($value === FALSE) {
+}
+
+if ($value == TRUE) {
+} else if ($value == FALSE) {
+}
+
+if ($value) {
+} else if (!$value) {
+}
+
+if (is_array($array) === TRUE) {
+} else if (myFunction($value) === FALSE) {
+}
+
+if (is_array($array) == TRUE) {
+} else if (myFunction($value) == FALSE) {
+}
+
+if (is_array($array)) {
+} else if (!myFunction($value)) {
+}
+
+if ($value === TRUE || $other === FALSE) {
+}
+
+if ($value == TRUE || $other == FALSE) {
+}
+
+if ($value || !$other) {
+}
+
+if ($one === TRUE || $two === TRUE || $three === FALSE || $four === TRUE) {
+}
+
+if ($one || $two || !$three || $four) {
+}
+
+if ($var instanceof PHP_CodeSniffer) {
+}
+
+if (($var instanceof PHP_CodeSniffer) === false) {
+}
+
+if ($good && ($var instanceof PHP_CodeSniffer) === false && $good) {
+}
+
+if ($good === true && ($var instanceof PHP_CodeSniffer) === false) {
+}
+
+// Without brackets around inline IF condition.
+$var1 === TRUE
+ ? $var2 = 0
+ : $var2 = 1;
+
+$var1 === TRUE ? $var2 = 0 : $var2 = 1;
+?>
+<?php
+$var1 === TRUE ? $var2 = 0 : $var2 = 1;
+
+if ($var2 === TRUE) {
+ $var1 === TRUE ? $var2 = 0 : $var2 = 1;
+}
+$var1 === TRUE ? $var2 = 0 : $var2 = 1;
+
+$var1
+ ? $var2 = 0
+ : $var2 = 1;
+
+$var1 ? $var2 = 0 : $var2 = 1;
+
+
+$var1 ? $var2 = 0 : $var2 = 1;
+
+if ($var2 === TRUE) {
+ $var1 ? $var2 = 0 : $var2 = 1;
+}
+$var1 ? $var2 = 0 : $var2 = 1;
+
+if ($value) {
+} elseif (!$value) {
+}
+
+if (false === ($parent instanceof Foo) && ($parent instanceof Bar) === false) {
+}
+
+if (false === ($parent instanceof Foo) && $foo) {
+}
+
+while ($var1) {
+}
+
+while ($var1 === TRUE) {
+}
+
+do {
+
+} while ($var1);
+
+do {
+
+} while ($var1 === TRUE);
+
+for ($var1 = 10; $var1; $var1--) {
+}
+
+for ($var1 = 10; $var1 !== 0; $var1--) {
+}
+
+for ($var1 = ($var2 === 10); $var1; $var1--) {
+}
+
+while (TRUE) {
+}
+
+while (FALSE) {
+}
+
+$var = ($var1 === true) ? $var1 : "foobar";
+
+$var = ($var1 == true) ? $var1 : "foobar";
+
+$var = ($var1 === false) ? $var1 : "foobar";
+
+$var = ($var1 == false) ? $var1 : "foobar";
+
+$var = ($var1 === 0) ? $var1 : "foobar";
+
+$var = ($var1 == 0) ? $var1 : "foobar";
+
+function foo(string $bar, array $baz, ?MyClass $object) : MyClass {}
--- /dev/null
+if (value === TRUE) {
+} else if (value === FALSE) {
+}
+
+if (value == TRUE) {
+} else if (value == FALSE) {
+}
+
+if (value) {
+} else if (!value) {
+}
+
+if (value.isSomething === TRUE) {
+} else if (myFunction(value) === FALSE) {
+}
+
+if (value.isSomething == TRUE) {
+} else if (myFunction(value) == FALSE) {
+}
+
+if (value.isSomething) {
+} else if (!myFunction(value)) {
+}
+
+if (value === TRUE || other === FALSE) {
+}
+
+if (value == TRUE || other == FALSE) {
+}
+
+if (value || !other) {
+}
+
+if (one === TRUE || two === TRUE || three === FALSE || four === TRUE) {
+}
+
+if (one || two || !three || four) {
+}
+
+while (one == true) {
+}
+
+while (one === true) {
+}
+
+do {
+} while (one == true);
+
+do {
+} while (one === true);
+
+for (one = 10; one != 0; one--) {
+}
+
+for (one = 10; one !== 0; one--) {
+}
+
+for (type in types) {
+}
+
+variable = (variable2 === true) ? variable1 : "foobar";
+
+variable = (variable2 == true) ? variable1 : "foobar";
+
+variable = (variable2 === false) ? variable1 : "foobar";
+
+variable = (variable2 == false) ? variable1 : "foobar";
+
+variable = (variable2 === 0) ? variable1 : "foobar";
+
+variable = (variable2 == 0) ? variable1 : "foobar";
--- /dev/null
+<?php
+/**
+ * Unit test class for the ComparisonOperatorUsage 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\Standards\Squiz\Tests\Operators;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ComparisonOperatorUsageUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='ComparisonOperatorUsageUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'ComparisonOperatorUsageUnitTest.inc':
+ return array(
+ 6 => 1,
+ 7 => 1,
+ 10 => 1,
+ 11 => 1,
+ 18 => 1,
+ 19 => 1,
+ 22 => 1,
+ 23 => 1,
+ 29 => 2,
+ 32 => 2,
+ 38 => 4,
+ 47 => 2,
+ 69 => 1,
+ 72 => 1,
+ 75 => 1,
+ 78 => 1,
+ 80 => 1,
+ 82 => 1,
+ 83 => 1,
+ 89 => 1,
+ 92 => 1,
+ 100 => 1,
+ 106 => 1,
+ 112 => 1,
+ 123 => 1,
+ 127 => 1,
+ 131 => 1,
+ );
+ break;
+ case 'ComparisonOperatorUsageUnitTest.js':
+ return array(
+ 5 => 1,
+ 6 => 1,
+ 17 => 1,
+ 18 => 1,
+ 28 => 2,
+ 40 => 1,
+ 47 => 1,
+ 52 => 1,
+ 63 => 1,
+ 67 => 1,
+ 71 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$var = ($var + 1);
+$var = ($one + 1);
+$var = ($var + 2);
+$var = ($one + $two + 1);
+$var += 1;
+$var += 2;
+$var += $one;
+$var += (1 + 2)
+$var = myFunction($var, ($one + 1));
+
+$var = ($var - 1);
+$var = ($one - 1);
+$var = ($var - 2);
+$var = ($one - $two - 1);
+$var -= 1;
+$var -= 2;
+$var -= $one;
+$var -= (1 + 2)
+$var = myFunction($var, ($one - 1));
+
+$var -= $var - 1;
+$var += $var - 1;
+
+$id = $id.'_'.($i-- - $x--).'_'.$x;
+$id = $id.'_'.(++$i - $x--).'_'.$x;
+$id = $id.'_'.(--$i - $x++).'_'.$x;
+
+$id = $id.'_'.$i++;
+$id = $id.'_'.($i++);
+$id = $id.'_'.$i--.'_';
+$id = $id.'_'.($i--).'_';
+
+$var = (1 - $var);
+$var = (1 + $var);
+
+$expected[$i]['sort_order'] = ($i + 1);
+$expected[($i + 1)]['sort_order'] = ($i + 1);
+
+$id = $id.($this->i++).$id;
--- /dev/null
+<?php
+/**
+ * Unit test class for the IncrementDecrementUsage 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\Standards\Squiz\Tests\Operators;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class IncrementDecrementUsageUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 6 => 1,
+ 12 => 1,
+ 16 => 1,
+ 25 => 1,
+ 26 => 1,
+ 27 => 1,
+ 29 => 1,
+ 31 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+if ($value === TRUE || $other === FALSE) {
+}
+
+if ($value === TRUE OR $other === FALSE) {
+}
+
+if ($value === TRUE && $other === FALSE) {
+}
+
+if ($value === TRUE and $other === FALSE) {
+}
+
+if ($one === TRUE && ($two === TRUE || $three === TRUE)) {
+}
+
+if ($one === TRUE AND ($two === TRUE or $three === TRUE)) {
+}
+
+if ($a ^ $b) {
+}
+
+if ($a xor $b) {
+}
+
+if ($a XOR $b) {
+}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the ValidLogicalOperators 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\Standards\Squiz\Tests\Operators;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ValidLogicalOperatorsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 5 => 1,
+ 11 => 1,
+ 17 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+/* CSS Document */
+
+body {
+font-family: Arial, Helvetica, sans-serif;
+margin : 40px 0 0 0;
+padding : 0;
+/*background: #8FB7DB url(login_glow_bg.jpg) no-repeat 30% 0;*/
+background: #8FB7DB url(diag_lines_bg.gif) top left;
+}
+
+#login-container {
+ margin-left: -225px;
+ margin-top: -161px;
+ position:absolute;
+ top :50%;
+ /*left :50%;*/
+ width:450px;
+}
+
+#cacheConfig-dayLabel, #cacheConfig-hourLabel, #cacheConfig-minuteLabel {
+ float: left;
+ padding-right: 8px;
+}
--- /dev/null
+<?php
+// This is a normal block comment that might
+// talk about functions like echo and print and if/else but
+// is not commented out code.
+
+// if (empty($this)) {echo 'This is will not work';}
+
+/*
+function myFunction()
+{
+
+}//end myFunction()
+*/
+
+//eval('$var = 4;');
+//$string = '$var = 4;';
+//eval($string);
+
+#eval('$var = 4;');
+#$string = '$var = 4;';
+#eval($string);
+
+if ($blah) {
+ // This is a multi-line comment that goes over two indented
+ // lines.
+
+ // If it's null, then there must be no parameters for this
+ // method.
+}
+
+// short.
+
+// Continue, as we're done with this token.
+
+/**
+ * The listeners array.
+ *
+ * @var array(PHP_CodeSniffer_Sniff)
+ */
+
+//
+
+/*
+*/
+
+function myFunction()
+{
+}//end myFunction()
+
+// ----------------------------
+// | A comment block |
+// ----------------------------
+
+// =============================
+// | A comment block |
+// =============================
+
+// *****************************
+// | A comment block |
+// *****************************
+
+/**
+ * List of panels.
+ *
+ * XML structure:
+ * <panels>
+ * <panel_one_id>
+ * <title>Title</title>
+ * <panelContent>Contents</panelContent>
+ * <panels>
+ * <child_panel_id>...</child_panel_id>
+ * </panels>
+ * </panel_one_id>
+ * </panels>
+ *
+ * @return void
+ */
+
+/*
+ [^\'"]
+*/
+
+// http://www.google.com
+
+// Base config function.
+
+// function ()
+
+// T_STRING is not a function call or not listed in _getFunctionListWithCallableArgument().
--- /dev/null
+<?php
+/**
+ * Unit test class for the CommentedOutCode 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class CommentedOutCodeUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList($testFile='CommentedOutCodeUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'CommentedOutCodeUnitTest.inc':
+ return array(
+ 6 => 1,
+ 8 => 1,
+ 15 => 1,
+ 19 => 1,
+ 87 => 1,
+ );
+ break;
+ case 'CommentedOutCodeUnitTest.css':
+ return array(
+ 7 => 1,
+ 16 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+trySomething() || trySomethingElse();
+if (trySomething() === FALSE) {
+ trySomethingElse();
+}
+
+$success || fail();
+if ($success === FALSE) {
+ fail();
+}
+
+$foo = ($bar || $foo);
+
+doSomething() || die();
+
+if ($something || somethingElse()) {
+ while ($foo && $bar) {
+ }
+
+ do {
+ // Code here.
+ }
+ while ($foo && $bar);
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowBooleanStatement 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowBooleanStatementUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 8 => 1,
+ 13 => 1,
+ 15 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$var = TRUE;
+$var = ($foo === $bar);
+$var = ($foo * $bar);
+$var = !$foo;
+$var = ($foo || $bar);
+$var = ($foo === TRUE);
+$var = ($foo === TRUE
+ || $bar === FALSE);
+$var = (!$foo);
+
+$var = is_array($foo);
+$var = myFunction($one, $two);
+$var = myFunction(
+ 'one',
+ 'two'
+ );
+
+for ($i = ($stackPtr + 1); $i < $endStatement; $i++) {
+}
+
+// These conditions are allowed by this sniff.
+$var = myFunction(!$var);
+
+$depthAdv = array(
+ $this,
+ !$directLinks,
+ FALSE,
+ );
+
+$var = myFunction(
+ $var,
+ array(
+ $this,
+ !$directLinks,
+ FALSE,
+ );
+);
+
+for ($node = $fieldsTag->nextSibling; $node; $node = $node->nextSibling) {
+ if ($node->nodeType !== XML_ELEMENT_NODE) {
+ continue;
+ }
+
+ for ($node = $fields->nextSibling; $node; $node = $node->nextSibling) {
+ if ($node->nodeType !== XML_ELEMENT_NODE) {
+ continue;
+ }
+ }
+}
+
+$a = $b ? $c : $d;
+$a = $b === true ? $c : $d;
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowComparisonAssignment 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowComparisonAssignmentUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 5 => 1,
+ 6 => 1,
+ 7 => 1,
+ 8 => 1,
+ 10 => 1,
+ 52 => 1,
+ 53 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+if ($var1) {
+ $var2 = 0;
+} else {
+ $var2 = 1;
+}
+
+$var1 ? $var2 = 0 : $var2 = 1;
+
+function foo(string $bar, array $baz, ?MyClass $object) : MyClass {}
--- /dev/null
+x = (x?a:x);
+id = id.replace(/row\/:/gi, '');
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowObEndFlush 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowInlineIfUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='DisallowInlineIfUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'DisallowInlineIfUnitTest.inc':
+ return array(8 => 1);
+ break;
+ case 'DisallowInlineIfUnitTest.js':
+ return array(1 => 1);
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+function getVar($varname='var', $var='var')
+{
+ $var = $var2 = getVar();
+ $var = $var2 = $var3 = '';
+
+ if ($varname === 'var' || $var2 === 'varname' || $var = true) {
+ $var[($var + 1)] = $var2;
+ $var[($var = 1)] = $var2;
+ }
+
+ for ($i = $var; (null !== ($key = key($i))); next($i)) {
+ while ($i = true) {
+ echo $i = getVar();
+ }
+ }
+
+ while ($row = $query->fetch(PDO::FETCH_NUM)) {
+ $result[$row[0]] = array()
+ $result[$row[0]][] = $current;
+
+ self::$_inTransaction = TRUE;
+ $$varName = $varValue;
+ }
+
+}//end getVar()
+
+class myClass
+{
+ private static $_dbh = NULL;
+ public $dbh = NULL;
+ protected $dbh = NULL;
+ var $dbh = NULL; // Old PHP4 compatible code.
+}
+
+$var = $var2;
+list ($a, $b) = explode(',', $c);
+$var1 ? $var2 = 0 : $var2 = 1;
+
+$obj->$classVar = $prefix.'-'.$type;
+
+$closureWithDefaultParamter = function(array $testArray=array()) {};
+?>
+<?php $var = false; ?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowMultipleAssignments 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowMultipleAssignmentsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 4 => 1,
+ 5 => 2,
+ 7 => 1,
+ 9 => 1,
+ 12 => 1,
+ 14 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+ob_start();
+ echo 'hello';
+ $contents = ob_get_contents();
+ob_end_clean();
+
+ob_start();
+ echo 'hello';
+ob_end_flush();
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowObEndFlush 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowObEndFlushUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(9 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+for ($i = 0; $i < count($array); $i++) {
+}
+
+$num = count($array);
+
+while ($i < count($array)) {
+}
+
+do {
+} while ($i < count($array));
+
+for ($i = 0; $i < count($this->children); $i++) {
+}
+
+
+
+for ($i = 0; $i < sizeof($array); $i++) {
+}
+
+$num = sizeof($array);
+
+while ($i < sizeof($array)) {
+}
+
+do {
+} while ($i < sizeof($array));
+
+for ($i = 0; $i < sizeof($this->children); $i++) {
+}
+
+
+
+
+for ($i = 0; $i < strlen($string); $i++) {
+}
+
+$num = strlen($string);
+
+while ($i < strlen($string)) {
+}
+
+do {
+} while ($i < strlen($string));
+
+for ($i = 0; $i < strlen($this->string); $i++) {
+}
+
+for ($i = sizeof($array); $i > 0; $i--) {
+}
+
+do {
+ echo $a->count;
+ $a->count--;
+} while($a->count);
+
+for ($i = 0; $i < $a->count; $i++) {}
+?>
--- /dev/null
+for (var i = 0; i < permissions.length; i++) {
+ // Code here.
+}
+
+var permLen = permissions.length;
+for (var length = 0; i < permLen; i++) {
+ // Code here.
+}
+
+var myArray = [1, 2, 3, 4];
+for (var i = myArray.length; i >= 0; i--) {
+ var x = i;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the DisallowSizeFunctionsInLoops 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowSizeFunctionsInLoopsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='DisallowSizeFunctionsInLoopsUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'DisallowSizeFunctionsInLoopsUnitTest.inc':
+ return array(
+ 2 => 1,
+ 7 => 1,
+ 11 => 1,
+ 13 => 1,
+ 18 => 1,
+ 23 => 1,
+ 27 => 1,
+ 29 => 1,
+ 35 => 1,
+ 40 => 1,
+ 44 => 1,
+ 46 => 1,
+ );
+ break;
+ case 'DisallowSizeFunctionsInLoopsUnitTest.js':
+ return array(1 => 1);
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+error_log('test');
+print_r($array);
+var_dump($array);
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the DiscouragedFunctions 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DiscouragedFunctionsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 2 => 1,
+ 3 => 1,
+ 4 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+ // Not embedded. Do not check here.
+ echo 'hello';
+?>
+<html>
+<head>
+<title><?php echo $title ?></title>
+<script><?php echo $script; ?></script>
+</head>
+<body>
+ <?php
+ echo $body;
+ ?>
+ hello
+ <?php
+ echo $moreBody;
+ ?>
+ <?php echo 'hi'; ?>
+ <?php echo 'hi'; ?>
+ <?php echo 'hi;' ?>
+ <?php echo 'hi'; echo 'hi;'; ?>
+ <?php echo 'hi'; echo 'hi;'; ?>
+
+ <?php
+ ?>
+ <?php ?>
+
+ <?php
+
+ echo $moreBody;
+
+ ?>
+ <?php
+
+ echo $moreBody;
+
+ ?>
+
+ <?php
+ echo $moreBody; ?>
+ <?php echo $moreBody;
+ ?>
+
+ <?php
+ echo 'hi';
+ ?>
+
+ <?php
+echo 'hi';
+ ?>
+</body>
+</html>
+<?php
+function test()
+{
+ foreach ($root->section as $section) {
+ ?>
+ <table>
+ <?php if ($foo) {
+ ?>
+ <tr>
+ </tr>
+ <?php }
+ ?>
+ <?php
+ foreach ($bar as $bar) {
+ echo $bar;
+ }
+ }
+
+ foreach ($root->section as $section) {
+ ?>
+ <table>
+ <?php
+ if ($foo) {
+ ?>
+ <tr>
+ </tr>
+ <?php
+}
+ ?>
+ <?php
+ foreach ($bar as $bar) {
+ echo $bar;
+ }
+ }
+}
+
+echo 'goodbye';
+
+function foo()
+{
+
+ ?><a onClick="Javascript: set_hidden_field('<?php echo $link_offset - $num_per_page; ?>'); set_hidden_field('process_form', '0'); submit_form(); return false;"><?php
+
+}
+
+?>
+
+ <strong><?php
+ echo 'foo';
+ ?></strong>
+
+?>
+
+</html>
+
+<?php if ($foo) { ?>
+<?php } ?>
+
+<?php echo 'oops'; // Something. ?>
+<?php echo 'oops'; // Something. ?>
+<?php echo 'oops'; // Something.?>
+
+<?php /* translators: My sites label */ ?>
+<?php /* translators: My sites label */?>
+<?php /* translators: My sites label */ ?>
--- /dev/null
+<?php
+ // Not embedded. Do not check here.
+ echo 'hello';
+?>
+<html>
+<head>
+<title><?php echo $title; ?></title>
+<script><?php echo $script; ?></script>
+</head>
+<body>
+ <?php
+ echo $body;
+ ?>
+ hello
+ <?php
+ echo $moreBody;
+ ?>
+ <?php echo 'hi'; ?>
+ <?php echo 'hi'; ?>
+ <?php echo 'hi;'; ?>
+ <?php echo 'hi'; echo 'hi;'; ?>
+ <?php echo 'hi'; echo 'hi;'; ?>
+
+
+ <?php
+ echo $moreBody;
+ ?>
+ <?php
+ echo $moreBody;
+ ?>
+
+ <?php
+ echo $moreBody;
+ ?>
+ <?php
+ echo $moreBody;
+ ?>
+
+ <?php
+ echo 'hi';
+ ?>
+
+ <?php
+ echo 'hi';
+ ?>
+</body>
+</html>
+<?php
+function test()
+{
+ foreach ($root->section as $section) {
+ ?>
+ <table>
+ <?php
+ if ($foo) {
+ ?>
+ <tr>
+ </tr>
+ <?php
+ }
+ ?>
+ <?php
+ foreach ($bar as $bar) {
+ echo $bar;
+ }
+ }
+
+ foreach ($root->section as $section) {
+ ?>
+ <table>
+ <?php
+ if ($foo) {
+ ?>
+ <tr>
+ </tr>
+ <?php
+}
+ ?>
+ <?php
+ foreach ($bar as $bar) {
+ echo $bar;
+ }
+ }
+}
+
+echo 'goodbye';
+
+function foo()
+{
+ ?>
+ <a onClick="Javascript: set_hidden_field('<?php echo $link_offset - $num_per_page; ?>'); set_hidden_field('process_form', '0'); submit_form(); return false;">
+ <?php
+
+}
+
+?>
+
+ <strong>
+ <?php
+ echo 'foo';
+ ?>
+ </strong>
+
+?>
+
+</html>
+
+<?php if ($foo) { ?>
+<?php } ?>
+
+<?php echo 'oops'; // Something. ?>
+<?php echo 'oops'; // Something. ?>
+<?php echo 'oops'; // Something. ?>
+
+<?php /* translators: My sites label */ ?>
+<?php /* translators: My sites label */ ?>
+<?php /* translators: My sites label */ ?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the EmbeddedPhp 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class EmbeddedPhpUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 7 => 1,
+ 12 => 1,
+ 18 => 1,
+ 19 => 2,
+ 20 => 1,
+ 21 => 1,
+ 22 => 3,
+ 24 => 1,
+ 26 => 1,
+ 29 => 1,
+ 30 => 1,
+ 31 => 1,
+ 34 => 1,
+ 36 => 1,
+ 40 => 1,
+ 41 => 1,
+ 44 => 1,
+ 45 => 1,
+ 49 => 1,
+ 59 => 1,
+ 63 => 1,
+ 93 => 1,
+ 94 => 2,
+ 100 => 1,
+ 102 => 1,
+ 112 => 1,
+ 113 => 1,
+ 116 => 1,
+ 117 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+eval('$var = 4;');
+$string = '$var = 4;';
+eval($string);
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the Eval 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class EvalUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 2 => 1,
+ 4 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
+print 'hi';
+print($var);
+if (is_null($blah) === TRUE) {
+}
+
+class Test
+{
+ const DELETE = 'delete';
+}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the ForbiddenFunctions 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ForbiddenFunctionsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 3 => 1,
+ 4 => 1,
+ 5 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$var = 'hello';
+$var2 = 'hello';
+$GLOBALS['var'] = 'hello';
+
+function func1()
+{
+ global $var;
+ global $var, $var2;
+ echo $var;
+ echo $GLOBALS['var'];
+}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the GlobalKeyword 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class GlobalKeywordUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 8 => 1,
+ 9 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$str = <<<EOD
+Example of string
+spanning multiple lines
+using heredoc syntax.
+EOD;
+
+echo <<<'EOT'
+My name is "$name". I am printing some $foo->foo.
+Now, I am printing some {$foo->bar[1]}.
+This should not print a capital 'A': \x41
+EOT;
+
+// The following function has a simulated git conflict for testing.
+// This is not a merge conflict - it is a valid test case.
+// Please do not remove.
+function test()
+ {
+ $arr = array(
+ 'a' => 'a'
+<<<<<<< HEAD
+ 'b' => 'b'
+=======
+ 'c' => 'c'
+>>>>>>> master
+ );
+ }
--- /dev/null
+<?php
+/**
+ * Unit test class for the Heredoc 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class HeredocUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 8 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+function outer()
+{
+ if (!function_exists('inner')) {
+ function inner() {
+ }
+ }
+}
+
+// Closures are allowed.
+function myFunc($foo)
+{
+ $callback = function ($bar) use ($foo)
+ {
+ $bar += $foo;
+ };
+}
+
+// Anon class methods are allowed.
+function test()
+{
+ return new class {
+
+ public function foo()
+ {
+ // do something
+ }
+ };
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the InnerFunctions 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class InnerFunctionsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(5 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+if (Function_Exists('myFunction') === TRUE) {
+ $retval = MyFunction(true);
+ $keys = Array_Keys($array);
+}
+
+function getType() {}
+
+$obj = new Date();
+
+$count = $object->Count();
+$count = $object::Count();
+$count = $object->count();
+$count = $object::count();
+class MyClass {
+ public function Count() {}
+}
+
+function &Sort() {
+
+}
+
+$connection = new Db\Adapter\Pdo\Mysql($config);
--- /dev/null
+<?php
+/**
+ * Unit test class for the LowercasePHPFunctions 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LowercasePHPFunctionsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 4 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+switch ($var) {
+ case '1':
+ return;
+ echo 'hi';
+
+ case '2':
+ case '3':
+ if ($something === true) {
+ break;
+ echo 'hi';
+ }
+ break;
+ default:
+ return;
+
+ if ($something === true) {
+ break;
+ echo 'hi';
+ }
+
+}
+
+function myFunction($var)
+{
+ if ($something === TRUE) {
+ return;
+ echo 'hi';
+ }
+
+ return;
+ return FALSE;
+ if ($something === TRUE) {
+ return TRUE;
+ }
+
+}//end myFunction()
+
+foreach ($vars as $var) {
+ if ($something === TRUE) {
+ continue;
+ break;
+ } else {
+ continue;
+ echo 'hi';
+ }
+
+ echo $var."\n";
+}
+
+switch ($lowerVarType) {
+ case 'bool':
+ return 'boolean';
+ echo 'hi';
+ case 'double':
+ case 'real':
+ return 'float';
+ echo 'hi';
+}
+
+while ($line=fgets($fp,2*1024*1024))
+{
+ if (!preg_match("/^<([a-z0-9_]+)/",$line,$matches))
+ continue;
+ print $line;
+}
+
+switch ($var) {
+ case 1:
+ echo '1';
+ break;
+
+ echo 'non-executable';
+ default:
+ echo '2';
+ break;
+}
+
+switch (0) {
+ case 1:
+ return '1';
+
+ echo 'non-executable';
+ default:
+ break;
+}
+
+function myFunction()
+{
+ if ($something === TRUE) {
+ return;
+ }
+
+ echo 'foo';
+ return;
+
+}//end myFunction()
+
+function myFunction()
+{
+ return uksort(
+ $array,
+ function() {
+ return mt_rand(-1, 1);
+ echo 'done';
+ }
+ );
+
+}//end myFunction()
+
+public static function thisCausesAnError() {
+return new foo(function() {return $foo;}
+);
+}
+
+function myFunction()
+{
+ if ($something === TRUE) {
+ throw new Exception('exception');
+ }
+
+ throw new Exception('exception');
+ echo 'non-executable';
+}//end myFunction()
+
+switch ($var) {
+ case 1: {
+ return '1';
+ }
+
+ case 2: {
+ return '2';
+ }
+}
+
+defined('FOO') or die('error');
+defined('FOO') || die('error');
+interface myInterface {
+ function myFunc();
+}
+echo 'hello';
+
+function foo($color) {
+ switch ($color) {
+ case 'red':
+ return 'yuck';
+ break;
+ case 'blue':
+ return 'yuck';
+ break;
+ case 'orange':
+ return 'yay';
+ break;
+ default:
+ return 'boring';
+ }
+}
+
+function returnOverMultipleLines($color) {
+ switch ($color) {
+ case 'red':
+ return someFunction(
+ 'multiple',
+ 'arguments'
+ );
+ echo $foo;
+ default:
+ return array(
+ 'multiline',
+ 'array'
+ );
+ }
+}
+
+function test() {
+ return array(
+ 'multiline',
+ 'array'
+ );
+ echo $foo;
+}
+
+function test(){
+ switch($a) {
+ case 1:
+ if (empty($b))
+ return 0;
+ break;
+ default:
+ return 2;
+ }
+
+ if (empty($a))
+ echo '1';
+ elseif ($empty($b))
+ return 0;
+ else
+ return 1;
+
+ echo "oi";
+ return 1;
+}
+
+switch ($foo) {
+ case 'foo':
+ if ($foo)
+ return $foo;
+ return $bar;
+ default:
+ return $bar;
+}
+
+function foo()
+{
+ return $bar->{$action . 'JsonAction'}();
+}
+
+switch (true) {
+ case 1:
+ return foo(
+ function () {
+ $foo = $bar; // when this is removed it works ok
+ return false; // from here on it reports unreachable
+ }
+ );
+}
+
+exit();
+
+
+// Errors are thrown from here down from the exit() above.
+foreach ($vars as $var) {
+ if ($something === TRUE) {
+ break;
+ break;
+ }
+}
+
+exit();
+
+function test() {
+ echo 'no error';
+}
+
+class myClass {
+ function myFunc() {
+ echo 'no error';
+ }
+}
+
+function bar() {
+ return function() {
+ echo "1";
+ };
+}
+
+class HttpStatus
+{
+ const CONTINUE = 100;
+ const SWITCHING_PROTOCOLS = 101;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the NonExecutableCode 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\Standards\Squiz\Tests\PHP;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class NonExecutableCodeUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 5 => 1,
+ 11 => 1,
+ 17 => 1,
+ 18 => 1,
+ 19 => 2,
+ 28 => 1,
+ 32 => 1,
+ 33 => 2,
+ 34 => 2,
+ 42 => 1,
+ 45 => 1,
+ 54 => 1,
+ 58 => 1,
+ 73 => 1,
+ 83 => 1,
+ 95 => 1,
+ 105 => 1,
+ 123 => 1,
+ 147 => 1,
+ 150 => 1,
+ 153 => 1,
+ 166 => 1,
+ 180 => 1,
+ 232 => 1,
+ 233 => 1,
+ 234 => 1,
+ 235 => 2,
+ 239 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class MyClass
+{
+ private $var1 = null;
+ protected $var2 = null;
+ public $var3 = null;
+ $var4 = null;
+}
+
+class foo
+{
+ const bar = <<<BAZ
+qux
+BAZ;
+}
+
+class Foo {
+ public static function default(): ObjectA
+ {
+ return new self(1);
+ }
+}//end class
--- /dev/null
+<?php
+/**
+ * Unit test class for the MemberVarScope 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\Standards\Squiz\Tests\Scope;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class MemberVarScopeUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(7 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+function func1() {}
+
+class MyClass
+{
+ function func1() {}
+ public function func1() {}
+ private function func1() {}
+ protected function func1() {}
+}
+
+class Closure_Test {
+ public function test() {
+ $foo = function() { echo 'foo'; };
+
+ function my_sort() {
+ // ...
+ }
+
+ $a = array();
+ usort($a, 'my_sort');
+ }
+}
+
+function test() {
+ $foo = function() { echo 'foo'; };
+}
+
+trait Trait_Test {
+ function func1() {}
+ public function func1() {}
+ private function func1() {}
+ protected function func1() {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the MethodScope 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\Standards\Squiz\Tests\Scope;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class MethodScopeUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 6 => 1,
+ 30 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class MyClass
+{
+ public static function func1()
+ {
+ $value = 'hello';
+ $newValue = array($this->func2());
+ $result = $this->getValue($value);
+ return $this->setValue($result);
+ }
+
+ public function func1()
+ {
+ $value = 'hello';
+ $newValue = array($this->func2());
+ $result = $this->getValue($value);
+ return $this->setValue($result);
+ }
+
+ function func1()
+ {
+ $value = 'hello';
+ $newValue = array($this->func2());
+ $result = $this->getValue($value);
+ return $this->setValue($result);
+ }
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the StaticThisUsage 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\Standards\Squiz\Tests\Scope;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class StaticThisUsageUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 7 => 1,
+ 8 => 1,
+ 9 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$string = 'Hello'.$there.'. How are'.$you.$going."today $okay";
+$string = 'Hello' . $there . '. How are' . $you . $going . "today $okay";
+$string = 'Hello'.$there;
+$string = 'Hello'. $there;
+$string = 'Hello' .$there;
+
+$y = '1'
+ . '2'
+ . '3';
+
+echo 1 . 'test';
+echo 1.1 . 'test';
+echo 'test' . 1;
+echo 'test' . 1.1;
+echo 'test' . 1 . 'test' . 2.2 . 'test' . 3;
+
+get_current_screen()->add_help_tab( array(
+'id' => <<<EOD
+Here comes some text.
+EOD
+. '</hr>',
+) );
+
+// @codingStandardsChangeSetting Squiz.Strings.ConcatenationSpacing spacing 1
+
+$string = 'Hello'.$there.'. How are'.$you.$going. "today $okay";
+$string = 'Hello' . $there . '. How are' . $you . $going . "today $okay";
+$string = 'Hello'.$there;
+$string = 'Hello'. $there;
+$string = 'Hello' .$there;
+
+// @codingStandardsChangeSetting Squiz.Strings.ConcatenationSpacing ignoreNewlines true
+$y = '1'
+ . '2'
+ . '3';
+
+$y = '1' .
+ '2' .
+ '3';
+
+$y = '1'
+. '2'
+. '3';
+
+$y = '1'
+ .'2'.
+ '3'
+ . '4';
--- /dev/null
+<?php
+$string = 'Hello'.$there.'. How are'.$you.$going."today $okay";
+$string = 'Hello'.$there.'. How are'.$you.$going."today $okay";
+$string = 'Hello'.$there;
+$string = 'Hello'.$there;
+$string = 'Hello'.$there;
+
+$y = '1'.'2'.'3';
+
+echo (1).'test';
+echo (1.1).'test';
+echo 'test'.(1);
+echo 'test'.(1.1);
+echo 'test'.(1).'test'.(2.2).'test'.(3);
+
+get_current_screen()->add_help_tab( array(
+'id' => <<<EOD
+Here comes some text.
+EOD
+.'</hr>',
+) );
+
+// @codingStandardsChangeSetting Squiz.Strings.ConcatenationSpacing spacing 1
+
+$string = 'Hello' . $there . '. How are' . $you . $going . "today $okay";
+$string = 'Hello' . $there . '. How are' . $you . $going . "today $okay";
+$string = 'Hello' . $there;
+$string = 'Hello' . $there;
+$string = 'Hello' . $there;
+
+// @codingStandardsChangeSetting Squiz.Strings.ConcatenationSpacing ignoreNewlines true
+$y = '1'
+ . '2'
+ . '3';
+
+$y = '1' .
+ '2' .
+ '3';
+
+$y = '1'
+. '2'
+. '3';
+
+$y = '1'
+ . '2' .
+ '3'
+ . '4';
--- /dev/null
+<?php
+/**
+ * Unit test class for the ConcatenationSpacing 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\Standards\Squiz\Tests\Strings;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ConcatenationSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 5,
+ 5 => 1,
+ 6 => 1,
+ 9 => 1,
+ 10 => 1,
+ 12 => 1,
+ 13 => 1,
+ 14 => 1,
+ 15 => 1,
+ 16 => 5,
+ 22 => 1,
+ 27 => 5,
+ 29 => 1,
+ 30 => 1,
+ 31 => 1,
+ 47 => 2,
+ 49 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$string = "Hello\tThere";
+$string = "Hello There\r\n";
+$string = "Hello There";
+$string = "Hello $there";
+$string = 'Hello'."$there '".'hi';
+$string = "My name is 'Greg'";
+$string = "Hello"." There"."\n";
+$string = "Hello There\f";
+$string = "Hello There\b";
+$string = "Hello\vThere";
+$string = "\x0";
+$string = 'Hello '.$there.' Greg';
+$string = "<div class='$class'>";
+$string = "Value: $var[test]";
+$string = "\0";
+$string = "\$var";
+
+$x = "bar = '$z',
+baz = '" . $a . "'...$x";
+
+$string = "Hello
+there";
+$string = 'Hello
+there';
+
+$string = "\123 \234"."\u123"."\e";
+
+echo "window.location = \"".$url."\";\n";
+echo ""
+
+$string = "Hello
+ there";
+
+function test() {
+ echo "It Worked';
+}
--- /dev/null
+<?php
+$string = "Hello\tThere";
+$string = "Hello There\r\n";
+$string = 'Hello There';
+$string = "Hello $there";
+$string = 'Hello'."$there '".'hi';
+$string = "My name is 'Greg'";
+$string = 'Hello'.' There'."\n";
+$string = "Hello There\f";
+$string = "Hello There\b";
+$string = "Hello\vThere";
+$string = "\x0";
+$string = 'Hello '.$there.' Greg';
+$string = "<div class='$class'>";
+$string = "Value: $var[test]";
+$string = "\0";
+$string = '$var';
+
+$x = "bar = '$z',
+baz = '" . $a . "'...$x";
+
+$string = 'Hello
+there';
+$string = 'Hello
+there';
+
+$string = "\123 \234"."\u123"."\e";
+
+echo 'window.location = "'.$url."\";\n";
+echo ''
+
+$string = 'Hello
+ there';
+
+function test() {
+ echo "It Worked';
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the DoubleQuoteUsage 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\Standards\Squiz\Tests\Strings;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DoubleQuoteUsageUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 4 => 1,
+ 5 => 1,
+ 6 => 1,
+ 8 => 2,
+ 14 => 1,
+ 15 => 1,
+ 17 => 1,
+ 19 => 1,
+ 20 => 1,
+ 22 => 1,
+ 29 => 1,
+ 30 => 1,
+ 32 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$string = 'hello';
+echo $string;
+echo 'hello';
+echo('hello');
+echo($hello);
+echo ('hello');
+echo ($hello);
+echo($y) ;
+echo ($loan_device->returndate == 0) ? 'Not returned' : date('d/m/Y', $loan_device->returndate);
+?>
+<p><?php echo ($foo) ? 'true' : 'false' ?></p>
+<p><?php echo ($foo) ?></p>
--- /dev/null
+<?php
+$string = 'hello';
+echo $string;
+echo 'hello';
+echo 'hello';
+echo $hello;
+echo 'hello';
+echo $hello;
+echo $y ;
+echo ($loan_device->returndate == 0) ? 'Not returned' : date('d/m/Y', $loan_device->returndate);
+?>
+<p><?php echo ($foo) ? 'true' : 'false' ?></p>
+<p><?php echo $foo ?></p>
--- /dev/null
+<?php
+/**
+ * Unit test class for the EchoedStrings 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\Standards\Squiz\Tests\Strings;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class EchoedStringsUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 5 => 1,
+ 6 => 1,
+ 7 => 1,
+ 8 => 1,
+ 9 => 1,
+ 13 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$var = (int) $var2;
+$var = ( int ) $var2;
+$var = (int ) $var2;
+$var = ( int) $var2;
+$var = ( int ) $var2;
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the CastSpacing 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class CastSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 4 => 1,
+ 5 => 1,
+ 6 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+if ($something) {
+}
+foreach ($this as $that) {
+}
+while (true) {
+ for ($i = 0; $i < 10; $i++) {
+ }
+ if ($something) {
+ }
+
+ foreach ($this as $that) {
+ do {
+ } while (true);
+
+ }
+}
+
+if ($one) {
+} else ($two) {
+} else if ($three) {
+} elseif ($four) {
+}
+if ($one) {
+} else ($two) {
+} else if ($three) {
+} elseif ($four) {
+}
+?>
+<table>
+ <tr>
+ <td align="center" valign="center">
+ <?php
+ foreach ($this->children as $child) {
+ // There should be no error after this
+ // foreach, because it is followed by a
+ // close PHP tag.
+ }
+ ?>
+ </td>
+ </tr>
+</table>
+<?php
+
+switch ($blah) {
+ case 'one':
+ if ($blah) {
+ // There are no spaces before break.
+ }
+ break;
+
+ default:
+ if ($blah) {
+ // There are no spaces before break.
+ }
+ break;
+}
+
+switch ($blah) {
+ case 'one':
+ if ($blah) {
+ // There are no spaces before break.
+ }
+ break;
+
+ default:
+ if ($blah) {
+ // Code here.
+ }
+}
+
+foreach ($blah as $var) {
+ if ($blah) {
+ }
+ break;
+}
+
+while (true) {
+ for ($i = 0; $i < 10; $i++) {
+
+ if ($something) {
+ }
+
+ }
+
+ foreach ($this as $that) {
+ do {
+
+ echo $i;
+ } while (true);
+ }
+}
+
+function myFunction()
+{
+ if ($blah) {
+ }
+
+}//end myFunction()
+
+foreach ($this->children as $child) {
+ echo $child;
+
+}
+
+if ($defaultPageDesign === 0
+ && $defaultCascade === TRUE
+ && $defaultChildDesign === 0
+) {
+ $settingsUpdated = FALSE;
+}
+
+foreach ( $blah as $var ) {
+ if ( $blah ) {
+ }
+}
+
+if (
+ $defaultPageDesign === 0
+ && $defaultCascade === TRUE
+ && $defaultChildDesign === 0
+) {
+ $settingsUpdated = FALSE;
+}
+
+$moo = 'blar';
+switch ($moo)
+{
+ case 'blar':
+ if ($moo === 'blar2') {
+ $moo = 'blar'
+ }
+ return $moo;
+
+ default:
+ $moo = 'moo';
+ break;
+}
+
+do {
+}
+while (true);
+
+try {
+ // Something
+} catch (Exception $e) {
+ // Something
+}
+
+try {
+
+ // Something
+
+} catch (Exception $e) {
+
+ // Something
+
+}
+
+if ($one) {
+}
+elseif ($two) {
+}
+// else if something
+else if ($three) {
+} // else do something
+else {
+}
+
+if ($one) {
+
+}
+
+do {
+ echo 'hi';
+} while ( $blah );
+
+if ($one) {
+}
+// No blank line here.
+if ($two) {
+}
+
+switch ($moo)
+{
+ case 'blar':
+ if ($moo === 'blar2') {
+ $moo = 'blar'
+ }
+
+ return $moo;
+}
+
+try {
+ // Something
+}
+catch (Exception $e) {
+ // Something
+}
+
+if ($foo) {
+
+
+ /**
+ * Comment
+ */
+ function foo() {
+ // Code here
+ }
+
+
+ /**
+ * Comment
+ */
+ class bar() {
+
+ }//end class
+
+
+}
+
+if (true) { // some comment goes here
+
+ echo 'foo';
+}
+
+if (true) { echo 'foo';
+
+ echo 'foo';
+}
+
+?>
+<?php foreach($formset['Fieldset'] as $fieldset): ?>
+
+ <?php foreach($fieldset['Field'] as $field):
+
+ echo 'foo';
+ ?>
+ <?php endforeach; ?>
+<?php endforeach; ?>
--- /dev/null
+<?php
+if ($something) {
+}
+
+foreach ($this as $that) {
+}
+
+while (true) {
+ for ($i = 0; $i < 10; $i++) {
+ }
+
+ if ($something) {
+ }
+
+ foreach ($this as $that) {
+ do {
+ } while (true);
+ }
+}
+
+if ($one) {
+} else ($two) {
+} else if ($three) {
+} elseif ($four) {
+}
+
+if ($one) {
+} else ($two) {
+} else if ($three) {
+} elseif ($four) {
+}
+?>
+<table>
+ <tr>
+ <td align="center" valign="center">
+ <?php
+ foreach ($this->children as $child) {
+ // There should be no error after this
+ // foreach, because it is followed by a
+ // close PHP tag.
+ }
+ ?>
+ </td>
+ </tr>
+</table>
+<?php
+
+switch ($blah) {
+ case 'one':
+ if ($blah) {
+ // There are no spaces before break.
+ }
+ break;
+
+ default:
+ if ($blah) {
+ // There are no spaces before break.
+ }
+ break;
+}
+
+switch ($blah) {
+ case 'one':
+ if ($blah) {
+ // There are no spaces before break.
+ }
+ break;
+
+ default:
+ if ($blah) {
+ // Code here.
+ }
+}
+
+foreach ($blah as $var) {
+ if ($blah) {
+ }
+
+ break;
+}
+
+while (true) {
+ for ($i = 0; $i < 10; $i++) {
+ if ($something) {
+ }
+ }
+
+ foreach ($this as $that) {
+ do {
+ echo $i;
+ } while (true);
+ }
+}
+
+function myFunction()
+{
+ if ($blah) {
+ }
+
+}//end myFunction()
+
+foreach ($this->children as $child) {
+ echo $child;
+}
+
+if ($defaultPageDesign === 0
+ && $defaultCascade === TRUE
+ && $defaultChildDesign === 0
+) {
+ $settingsUpdated = FALSE;
+}
+
+foreach ($blah as $var) {
+ if ($blah) {
+ }
+}
+
+if ($defaultPageDesign === 0
+ && $defaultCascade === TRUE
+ && $defaultChildDesign === 0
+) {
+ $settingsUpdated = FALSE;
+}
+
+$moo = 'blar';
+switch ($moo)
+{
+ case 'blar':
+ if ($moo === 'blar2') {
+ $moo = 'blar'
+ }
+ return $moo;
+
+ default:
+ $moo = 'moo';
+ break;
+}
+
+do {
+}
+while (true);
+
+try {
+ // Something
+} catch (Exception $e) {
+ // Something
+}
+
+try {
+ // Something
+} catch (Exception $e) {
+ // Something
+}
+
+if ($one) {
+}
+elseif ($two) {
+}
+// else if something
+else if ($three) {
+} // else do something
+else {
+}
+
+if ($one) {
+}
+
+do {
+ echo 'hi';
+} while ($blah);
+
+if ($one) {
+}
+
+// No blank line here.
+if ($two) {
+}
+
+switch ($moo)
+{
+ case 'blar':
+ if ($moo === 'blar2') {
+ $moo = 'blar'
+ }
+ return $moo;
+}
+
+try {
+ // Something
+}
+catch (Exception $e) {
+ // Something
+}
+
+if ($foo) {
+
+
+ /**
+ * Comment
+ */
+ function foo() {
+ // Code here
+ }
+
+
+ /**
+ * Comment
+ */
+ class bar() {
+
+ }//end class
+
+
+}
+
+if (true) { // some comment goes here
+ echo 'foo';
+}
+
+if (true) { echo 'foo';
+
+ echo 'foo';
+}
+
+?>
+<?php foreach($formset['Fieldset'] as $fieldset): ?>
+ <?php foreach($fieldset['Field'] as $field):
+ echo 'foo';
+ ?>
+ <?php endforeach; ?>
+<?php endforeach; ?>
--- /dev/null
+
+if (something) {
+}
+for (i = 0; i < 10; i++) {
+}
+
+while (true) {
+ for (i = 0; i < 10; i++) {
+ }
+ if (something) {
+ }
+
+ do {
+ } while (true);
+
+}
+
+if (one) {
+} else (two) {
+} else if (three) {
+}
+if (one) {
+} else (two) {
+} else if (three) {
+}
+
+switch (blah) {
+ case 'one':
+ if (blah) {
+ // There are no spaces before break.
+ }
+ break;
+
+ default:
+ if (blah) {
+ // There are no spaces before break.
+ }
+ break;
+}
+
+switch (blah) {
+ case 'one':
+ if (blah) {
+ // There are no spaces before break.
+ }
+ break;
+
+ default:
+ if (blah) {
+ // Code here.
+ }
+}
+
+for (i = 0; i < 10; i++) {
+ if (blah) {
+ }
+ break;
+}
+
+while (true) {
+ for (i = 0; i < 10; i++) {
+
+ if (something) {
+ }
+
+ }
+
+ do {
+
+ alert(i);
+ } while (true);
+}
+
+for ( i = 0; i < 10; i++ ) {
+ if ( blah ) {
+ }
+}
+
+var x = {
+ a: function () {
+ if (blah) {
+ }
+
+ },
+};
+
+if (one) {
+}
+// else if something
+else if (two) {
+} // else do something
+else {
+}
--- /dev/null
+
+if (something) {
+}
+
+for (i = 0; i < 10; i++) {
+}
+
+while (true) {
+ for (i = 0; i < 10; i++) {
+ }
+
+ if (something) {
+ }
+
+ do {
+ } while (true);
+}
+
+if (one) {
+} else (two) {
+} else if (three) {
+}
+
+if (one) {
+} else (two) {
+} else if (three) {
+}
+
+switch (blah) {
+ case 'one':
+ if (blah) {
+ // There are no spaces before break.
+ }
+ break;
+
+ default:
+ if (blah) {
+ // There are no spaces before break.
+ }
+ break;
+}
+
+switch (blah) {
+ case 'one':
+ if (blah) {
+ // There are no spaces before break.
+ }
+ break;
+
+ default:
+ if (blah) {
+ // Code here.
+ }
+}
+
+for (i = 0; i < 10; i++) {
+ if (blah) {
+ }
+
+ break;
+}
+
+while (true) {
+ for (i = 0; i < 10; i++) {
+ if (something) {
+ }
+ }
+
+ do {
+ alert(i);
+ } while (true);
+}
+
+for (i = 0; i < 10; i++) {
+ if (blah) {
+ }
+}
+
+var x = {
+ a: function () {
+ if (blah) {
+ }
+
+ },
+};
+
+if (one) {
+}
+// else if something
+else if (two) {
+} // else do something
+else {
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ControlStructureSpacing 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ControlStructureSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='ControlStructureSpacingUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'ControlStructureSpacingUnitTest.inc':
+ return array(
+ 3 => 1,
+ 5 => 1,
+ 8 => 1,
+ 15 => 1,
+ 23 => 1,
+ 74 => 1,
+ 79 => 1,
+ 82 => 1,
+ 83 => 1,
+ 87 => 1,
+ 103 => 1,
+ 113 => 2,
+ 114 => 2,
+ 118 => 1,
+ 150 => 1,
+ 153 => 1,
+ 154 => 1,
+ 157 => 1,
+ 170 => 1,
+ 176 => 2,
+ 179 => 1,
+ 189 => 1,
+ 222 => 1,
+ 233 => 1,
+ 235 => 1,
+ );
+ break;
+ case 'ControlStructureSpacingUnitTest.js':
+ return array(
+ 3 => 1,
+ 9 => 1,
+ 15 => 1,
+ 21 => 1,
+ 56 => 1,
+ 61 => 1,
+ 64 => 1,
+ 65 => 1,
+ 68 => 1,
+ 74 => 2,
+ 75 => 2,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+function MyFunction1() {
+ // Some code goes here.
+
+}
+
+function MyFunction2() {
+ // Some code goes here.
+}
+
+class MyClass
+{
+ function MyFunction3() {
+ // Some code goes here.
+
+ }
+
+ function MyFunction4() {
+ // Some code goes here.
+ }
+
+ public function register();
+}
+
+public function register();
+
+function foo() { }
+function foo() {}
+function foo() {
+}
+
+function foo()
+{
+ for ($i = 0; $i < $bar; $i++) {
+ }//end for
+
+
+}
--- /dev/null
+<?php
+
+function MyFunction1() {
+ // Some code goes here.
+
+}
+
+function MyFunction2() {
+ // Some code goes here.
+
+}
+
+class MyClass
+{
+ function MyFunction3() {
+ // Some code goes here.
+
+ }
+
+ function MyFunction4() {
+ // Some code goes here.
+
+ }
+
+ public function register();
+}
+
+public function register();
+
+function foo() {
+
+ }
+function foo() {
+
+}
+function foo() {
+
+}
+
+function foo()
+{
+ for ($i = 0; $i < $bar; $i++) {
+ }//end for
+
+}
--- /dev/null
+function FuncOne()
+{
+ // Code here.
+
+}//end AdjustModalDialogWidgetType
+
+
+Testing.prototype = {
+
+ doSomething: function()
+ {
+ // Code here.
+ },
+
+ doSomethingElse: function()
+ {
+ // Code here.
+
+ },
+};
+
+function FuncFour()
+{
+ // Code here.
+}
+
+function FuncFive()
+{
+ // Code here.
+
+
+}
+
+function valid()
+{
+ if (true) {
+ test = {
+ namespaces: {}
+ };
+ }
+
+}
+
+dfx.addEvent(this.rightScroll, 'mousedown', function() {
+ t = setInterval(function() {
+ pos -= 10;
+ }, 30);
+});
+
+// Valid because function is empty.
+if (dfx.isFn(callback) === false) {
+ callback = function() {};
+ callback = function() { };
+}
+
+AbstractAttributeEditorWidgetType.prototype = {
+ isActive: function() {
+ return this.active;
+ },
+
+ activate: function(data)
+ {
+ var x = {
+ test: function () {
+ alert('This is ok');
+
+ }
+ };
+
+ this.active = true;
+
+ }
+
+};
+
+var myFunc = function()
+{
+ var x = 1;
+
+ blah(function() {
+ alert(2);
+ });
+
+ blah(function() { alert(2); });
+
+ return x;
+
+}
+
+a.prototype = {
+
+ a: function()
+ {
+ var settings = {
+ default: ''
+ };
+
+ },
+
+ b: function()
+ {
+ var self = this;
+
+ }
+
+};
+
+var a = new function()
+{
+ this.initScreen = function(usersFolderid)
+ {
+ for (var i = 0; i < paramSelectors.length; i++) {
+ }//end for
+ }
+
+};
+
+a.prototype = {
+
+ this.addItem(
+ id,
+ {
+ b: function()
+ {
+ for (var i = 0; i < paramSelectors.length; i++) {
+ }//end for
+
+ }
+ }
+ )
+
+};
--- /dev/null
+function FuncOne()
+{
+ // Code here.
+
+}//end AdjustModalDialogWidgetType
+
+
+Testing.prototype = {
+
+ doSomething: function()
+ {
+ // Code here.
+
+ },
+
+ doSomethingElse: function()
+ {
+ // Code here.
+
+ },
+};
+
+function FuncFour()
+{
+ // Code here.
+
+}
+
+function FuncFive()
+{
+ // Code here.
+
+}
+
+function valid()
+{
+ if (true) {
+ test = {
+ namespaces: {}
+ };
+ }
+
+}
+
+dfx.addEvent(this.rightScroll, 'mousedown', function() {
+ t = setInterval(function() {
+ pos -= 10;
+ }, 30);
+});
+
+// Valid because function is empty.
+if (dfx.isFn(callback) === false) {
+ callback = function() {};
+ callback = function() {};
+}
+
+AbstractAttributeEditorWidgetType.prototype = {
+ isActive: function() {
+ return this.active;
+
+ },
+
+ activate: function(data)
+ {
+ var x = {
+ test: function () {
+ alert('This is ok');
+ }
+ };
+
+ this.active = true;
+
+ }
+
+};
+
+var myFunc = function()
+{
+ var x = 1;
+
+ blah(function() {
+ alert(2);
+ });
+
+ blah(function() { alert(2);
+});
+
+ return x;
+
+}
+
+a.prototype = {
+
+ a: function()
+ {
+ var settings = {
+ default: ''
+ };
+
+ },
+
+ b: function()
+ {
+ var self = this;
+
+ }
+
+};
+
+var a = new function()
+{
+ this.initScreen = function(usersFolderid)
+ {
+ for (var i = 0; i < paramSelectors.length; i++) {
+ }//end for
+ }
+
+};
+
+a.prototype = {
+
+ this.addItem(
+ id,
+ {
+ b: function()
+ {
+ for (var i = 0; i < paramSelectors.length; i++) {
+ }//end for
+ }
+ }
+ )
+
+};
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionClosingBraceSpace 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionClosingBraceSpaceUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='FunctionClosingBraceSpaceUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'FunctionClosingBraceSpaceUnitTest.inc':
+ return array(
+ 10 => 1,
+ 21 => 1,
+ 28 => 1,
+ 29 => 1,
+ 31 => 1,
+ 39 => 1,
+ );
+ break;
+ case 'FunctionClosingBraceSpaceUnitTest.js':
+ return array(
+ 13 => 1,
+ 25 => 1,
+ 32 => 1,
+ 53 => 1,
+ 59 => 1,
+ 67 => 1,
+ 84 => 1,
+ 128 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+function MyFunction1()
+{
+ // Some code goes here.
+ echo $code;
+}
+
+function MyFunction2()
+{
+
+ // Some code goes here.
+ echo $code;
+}
+
+class MyClass
+{
+ function MyFunction1()
+ {
+ // Some code goes here.
+ echo $code;
+ }
+
+ function MyFunction2()
+ {
+
+ // Some code goes here.
+ echo $code;
+ }
+
+ public function register();
+}
+
+function emptyFunction()
+{
+
+}//end emptyFunction();
+
+public function register();
+?>
--- /dev/null
+function FuncOne()
+{
+ // Code here.
+
+}//end AdjustModalDialogWidgetType
+
+
+Testing.prototype = {
+
+ doSomething: function()
+ {
+
+ // Code here.
+
+ },
+
+ doSomethingElse: function()
+ {
+ // Code here.
+
+ },
+
+ start: function()
+ {
+ this.toolbarPlugin.addButton('Image', 'imageEditor', 'Insert/Edit Image', function () { self.editImage() });
+
+ },
+};
+
+function FuncFour()
+{
+
+
+ // Code here.
+}
+
+AbstractAttributeEditorWidgetType.prototype = {
+ isActive: function() {
+
+ return this.active;
+
+ },
+
+ activate: function(data)
+ {
+ var x = {
+ test: function () {
+ alert('This is ok');
+ }
+ };
+
+ this.active = true;
+
+ }
+
+};
+
+function test() {
+ var x = 1;
+ var y = function()
+ {
+ alert(1);
+ }
+
+ return x;
+
+}
+
+var myFunc = function()
+{
+ var x = 1;
+
+ blah(x, y, function()
+ {
+ alert(2);
+ }, z);
+
+ blah(function() { alert(2); });
+
+ return x;
+
+}
+
+HelpWidgetType.prototype = {
+ init: function() {
+ var x = 1;
+ var y = {
+ test: function() {
+
+
+ alert(3);
+ }
+ }
+ return x;
+
+ }
+}
+
+CustomFormEditWidgetType.prototype = {
+
+ addQuestion: function()
+ {
+ var settings = {
+ default: ''
+ };
+
+ },
+
+ addQuestionRulesEvent: function()
+ {
+ var self = this;
+
+ }
+
+};
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionOpeningBraceSpace 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionOpeningBraceSpaceUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='FunctionOpeningBraceSpaceUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'FunctionOpeningBraceSpaceUnitTest.inc':
+ return array(
+ 10 => 1,
+ 25 => 1,
+ );
+ break;
+ case 'FunctionOpeningBraceSpaceUnitTest.js':
+ return array(
+ 11 => 1,
+ 31 => 1,
+ 38 => 1,
+ 88 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+* Comment
+*/
+
+
+include $blah;
+
+
+/**
+* Comment
+*/
+
+
+/**
+* Comment
+*/
+function foo()
+{
+}//end foo()
+
+function func1() {
+
+}//end func1()
+
+
+function func2() {
+
+}//end func2()
+
+function func3() {
+
+}//end func3()
+
+
+class MyClass
+{
+ function func1() {
+
+ }//end func1()
+
+
+ function func2() {
+
+ }//end func2()
+
+ function func3() {
+
+ }//end func3()
+}//end class
+
+
+interface MyInterface
+{
+ function func1();
+
+
+ function func2();
+
+ function func3();
+}//end interface
+
+
+class MyClass
+{
+
+
+ function func1() {
+
+ }//end func1()
+
+
+ function func2() {
+
+ }//end func2()
+
+
+
+ function func3() {
+
+ }//end func3()
+
+
+}//end class
+
+
+interface MyInterface
+{
+
+
+ function func1();
+
+
+ function func2();
+
+
+
+ function func3();
+
+
+}//end interface
+
+class MyClass
+{
+ function func1() {
+
+ }//end func1()
+}//end class
+
+
+interface MyInterface
+{
+ function func1();
+}//end interface
+
+
+class MyClass
+{
+
+ /**
+ * The tag that this element represents (omiting the @ symbol).
+ *
+ * @var string
+ */
+ protected $tag = '';
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func1() {
+
+ }//end func1()
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func2() {
+
+ }//end func2()
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func3() {
+
+ }//end func3()
+
+}//end class
+
+
+interface MyInterface
+{
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func1();
+
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func2();
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func3();
+
+}//end interface
+
+class MyClass
+{
+
+ protected $tag = '';
+
+ protected $tag = '';
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func1() {
+
+ }//end func1()
+
+
+}//end class
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.FunctionSpacing spacing 1
+
+interface MyInterface
+{
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func1();
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func2();
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func3();
+
+
+}//end interface
+
+class MyClass
+{
+
+ protected $tag = '';
+
+ protected $tag = '';
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func1() {
+
+ }//end func1()
+
+
+}//end class
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.FunctionSpacing spacing 2
+
+// Closures should be ignored.
+preg_replace_callback(
+ '~-([a-z])~',
+ function ($match) {
+ return strtoupper($match[1]);
+ },
+ 'hello-world'
+);
+
+$callback = function ($bar) use ($foo)
+ {
+ $bar += $foo;
+ };
+
+class MyClass
+{
+ function func1() {
+ }
+}
+
+
+function test() {
+
+}//end test()
+
+
+if ($foo) {
+ /**
+ * Comment
+ */
+ function foo() {
+ // Code here
+ }
+ /**
+ * Comment
+ */
+ function bar() {
+ // Code here
+ }
+}
+
+// foo
+function foo() {
+}
--- /dev/null
+<?php
+/**
+* Comment
+*/
+
+
+include $blah;
+
+
+/**
+* Comment
+*/
+
+
+/**
+* Comment
+*/
+function foo()
+{
+}//end foo()
+
+
+function func1() {
+
+}//end func1()
+
+
+function func2() {
+
+}//end func2()
+
+
+function func3() {
+
+}//end func3()
+
+
+class MyClass
+{
+
+
+ function func1() {
+
+ }//end func1()
+
+
+ function func2() {
+
+ }//end func2()
+
+
+ function func3() {
+
+ }//end func3()
+
+
+}//end class
+
+
+interface MyInterface
+{
+
+
+ function func1();
+
+
+ function func2();
+
+
+ function func3();
+
+
+}//end interface
+
+
+class MyClass
+{
+
+
+ function func1() {
+
+ }//end func1()
+
+
+ function func2() {
+
+ }//end func2()
+
+
+ function func3() {
+
+ }//end func3()
+
+
+}//end class
+
+
+interface MyInterface
+{
+
+
+ function func1();
+
+
+ function func2();
+
+
+ function func3();
+
+
+}//end interface
+
+class MyClass
+{
+
+
+ function func1() {
+
+ }//end func1()
+
+
+}//end class
+
+
+interface MyInterface
+{
+
+
+ function func1();
+
+
+}//end interface
+
+
+class MyClass
+{
+
+ /**
+ * The tag that this element represents (omiting the @ symbol).
+ *
+ * @var string
+ */
+ protected $tag = '';
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func1() {
+
+ }//end func1()
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func2() {
+
+ }//end func2()
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func3() {
+
+ }//end func3()
+
+
+}//end class
+
+
+interface MyInterface
+{
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func1();
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func2();
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func3();
+
+
+}//end interface
+
+class MyClass
+{
+
+ protected $tag = '';
+
+ protected $tag = '';
+
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func1() {
+
+ }//end func1()
+
+
+}//end class
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.FunctionSpacing spacing 1
+
+interface MyInterface
+{
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func1();
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func2();
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func3();
+
+}//end interface
+
+class MyClass
+{
+
+ protected $tag = '';
+
+ protected $tag = '';
+
+ /**
+ * Function comment.
+ *
+ * @return boolean
+ */
+ function func1() {
+
+ }//end func1()
+
+}//end class
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.FunctionSpacing spacing 2
+
+// Closures should be ignored.
+preg_replace_callback(
+ '~-([a-z])~',
+ function ($match) {
+ return strtoupper($match[1]);
+ },
+ 'hello-world'
+);
+
+$callback = function ($bar) use ($foo)
+ {
+ $bar += $foo;
+ };
+
+class MyClass
+{
+
+
+ function func1() {
+ }
+
+
+}
+
+
+function test() {
+
+}//end test()
+
+
+if ($foo) {
+
+
+ /**
+ * Comment
+ */
+ function foo() {
+ // Code here
+ }
+
+
+ /**
+ * Comment
+ */
+ function bar() {
+ // Code here
+ }
+
+
+}
+
+// foo
+function foo() {
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the FunctionSpacing 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class FunctionSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 20 => 1,
+ 29 => 1,
+ 38 => 1,
+ 45 => 1,
+ 49 => 1,
+ 55 => 1,
+ 58 => 1,
+ 60 => 1,
+ 75 => 1,
+ 94 => 1,
+ 105 => 1,
+ 107 => 1,
+ 113 => 2,
+ 135 => 1,
+ 154 => 1,
+ 167 => 2,
+ 184 => 1,
+ 218 => 1,
+ 233 => 1,
+ 252 => 1,
+ 275 => 1,
+ 276 => 1,
+ 289 => 1,
+ 291 => 1,
+ 297 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+echo $blah;
+echo $blah;
+echo($blah);
+
+print $blah;
+print $blah;
+print($blah);
+
+include $blah;
+include $blah;
+include($blah);
+
+include_once $blah;
+include_once $blah;
+include_once($blah);
+
+require $blah;
+require $blah;
+require($blah);
+
+require_once $blah;
+require_once $blah;
+require_once($blah);
+
+$obj = new MyClass();
+$obj = new MyClass();
+
+return;
+return $blah;
+return $blah;
+return($blah);
--- /dev/null
+<?php
+echo $blah;
+echo $blah;
+echo($blah);
+
+print $blah;
+print $blah;
+print($blah);
+
+include $blah;
+include $blah;
+include($blah);
+
+include_once $blah;
+include_once $blah;
+include_once($blah);
+
+require $blah;
+require $blah;
+require($blah);
+
+require_once $blah;
+require_once $blah;
+require_once($blah);
+
+$obj = new MyClass();
+$obj = new MyClass();
+
+return;
+return $blah;
+return $blah;
+return($blah);
--- /dev/null
+<?php
+/**
+ * Unit test class for the LanguageConstructSpacing 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LanguageConstructSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 7 => 1,
+ 11 => 1,
+ 15 => 1,
+ 19 => 1,
+ 23 => 1,
+ 27 => 1,
+ 31 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+if ($foo || $bar) {}
+if ($foo||$bar && $baz) {}
+if ($foo|| $bar&&$baz) {}
+if ($foo || $bar && $baz) {}
+
+// Spacing after || below is ignored as it is EOL whitespace.
+if ($foo ||
+ $bar
+ && $baz
+) {
+}
+
+if ($foo||
+ $bar
+ &&$baz
+) {
+}
+
+?>
--- /dev/null
+
+
+if (foo || bar) {}
+if (foo||bar && baz) {}
+if (foo|| bar&&baz) {}
+if (foo || bar && baz) {}
+
+// Spacing after || below is ignored as it is EOL whitespace.
+if (foo ||
+ bar
+ && baz
+) {
+}
+
+if (foo||
+ bar
+ &&baz
+) {
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the LogicalOperatorSpacing 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class LogicalOperatorSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='LogicalOperatorSpacingUnitTest.inc')
+ {
+ return array(
+ 4 => 2,
+ 5 => 3,
+ 6 => 3,
+ 15 => 1,
+ 17 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class MyClass
+{
+ public $var1 = 'value';
+
+
+ public $var2 = 'value';
+
+ public $var3 = 'value';
+
+}//end class
+
+
+interface MyInterface
+{
+
+ public $var1 = 'value';
+
+
+ public $var2 = 'value';
+
+ protected $var3 = 'value';
+}//end interface
+
+
+class MyClass
+{
+
+
+ public $var1 = 'value';
+
+ private $var2 = 'value';
+
+
+ protected $var3 = 'value';
+
+
+}//end class
+
+
+
+class MyClass
+{
+ public $var1 = 'value';
+}//end class
+
+
+interface MyInterface
+{
+ public $var1 = 'value';
+ function myFunction();
+}//end interface
+
+
+class MyClass
+{
+
+ /**
+ * The actions that this wizard step requires.
+ *
+ * @var array
+ * @since 4.0.0
+ */
+ protected $actions = array();
+
+
+ /**
+ * TRUE if this step should be performed after the asset is created.
+ *
+ * @var boolean
+ * @since 4.0.0
+ */
+ protected $postStep = FALSE;
+
+
+}//end class
+
+class MyClass
+{
+ /**
+ * The actions that this wizard step requires.
+ *
+ * @var array
+ * @since 4.0.0
+ */
+ protected $actions = array();
+
+}//end class
+
+class MyClass
+{
+
+ /**
+ * The actions that this wizard step requires.
+ *
+ * @var array
+ * @since 4.0.0
+ */
+ var $actions = array();
+
+ /**
+ * The actions that this wizard step requires.
+ *
+ * @var array
+ * @since 4.0.0
+ */
+
+ protected $actions = array();
+ /**
+ * The actions that this wizard step requires.
+ *
+ * @var array
+ * @since 4.0.0
+ */
+ protected $actions = array();
+
+}//end class
+
+class Foo
+{
+
+ private $foo; // comment
+
+ private $bar;
+
+}
+
+class Foo
+{
+
+ private $foo; // comment
+
+ /**
+ * @var type
+ */
+ private $bar;
+
+}
+
+class Foo
+{
+
+ /**
+ * @var integer
+ */
+ private $foo; // comment
+
+ private $bar;
+
+ // here comes the comment
+
+ private $caseStudy = null;
+
+}
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.MemberVarSpacing spacing 2
+
+class MyClass
+{
+ public $var1 = 'value';
+
+
+ public $var2 = 'value';
+
+ public $var3 = 'value';
+
+}//end class
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.MemberVarSpacing spacing 1
+// @codingStandardsChangeSetting Squiz.WhiteSpace.MemberVarSpacing spacingBeforeFirst 0
+
+class MyClass
+{
+ public $var1 = 'value';
+
+
+ public $var2 = 'value';
+
+ public $var3 = 'value';
+
+}//end class
+
+class MyClass
+{
+
+ public $var1 = 'value';
+
+ public $var2 = 'value';
+
+ public $var3 = 'value';
+
+}//end class
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.MemberVarSpacing spacing 0
+
+class MyClass
+{
+ public $var1 = 'value';
+
+ public $var2 = 'value';
+ public $var3 = 'value';
+
+}//end class
+
+interface MyInterface
+{
+
+ /* testing */
+ public $var1 = 'value';
+
+ public $var2 = 'value';
+ public $var3 = 'value';
+
+}//end class
--- /dev/null
+<?php
+class MyClass
+{
+
+ public $var1 = 'value';
+
+ public $var2 = 'value';
+
+ public $var3 = 'value';
+
+}//end class
+
+
+interface MyInterface
+{
+
+ public $var1 = 'value';
+
+ public $var2 = 'value';
+
+ protected $var3 = 'value';
+}//end interface
+
+
+class MyClass
+{
+
+ public $var1 = 'value';
+
+ private $var2 = 'value';
+
+ protected $var3 = 'value';
+
+
+}//end class
+
+
+
+class MyClass
+{
+
+ public $var1 = 'value';
+}//end class
+
+
+interface MyInterface
+{
+
+ public $var1 = 'value';
+ function myFunction();
+}//end interface
+
+
+class MyClass
+{
+
+ /**
+ * The actions that this wizard step requires.
+ *
+ * @var array
+ * @since 4.0.0
+ */
+ protected $actions = array();
+
+ /**
+ * TRUE if this step should be performed after the asset is created.
+ *
+ * @var boolean
+ * @since 4.0.0
+ */
+ protected $postStep = FALSE;
+
+
+}//end class
+
+class MyClass
+{
+
+ /**
+ * The actions that this wizard step requires.
+ *
+ * @var array
+ * @since 4.0.0
+ */
+ protected $actions = array();
+
+}//end class
+
+class MyClass
+{
+
+ /**
+ * The actions that this wizard step requires.
+ *
+ * @var array
+ * @since 4.0.0
+ */
+ var $actions = array();
+
+ /**
+ * The actions that this wizard step requires.
+ *
+ * @var array
+ * @since 4.0.0
+ */
+ protected $actions = array();
+
+ /**
+ * The actions that this wizard step requires.
+ *
+ * @var array
+ * @since 4.0.0
+ */
+ protected $actions = array();
+
+}//end class
+
+class Foo
+{
+
+ private $foo; // comment
+
+ private $bar;
+
+}
+
+class Foo
+{
+
+ private $foo; // comment
+
+ /**
+ * @var type
+ */
+ private $bar;
+
+}
+
+class Foo
+{
+
+ /**
+ * @var integer
+ */
+ private $foo; // comment
+
+ private $bar;
+
+ // here comes the comment
+ private $caseStudy = null;
+
+}
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.MemberVarSpacing spacing 2
+
+class MyClass
+{
+
+ public $var1 = 'value';
+
+
+ public $var2 = 'value';
+
+
+ public $var3 = 'value';
+
+}//end class
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.MemberVarSpacing spacing 1
+// @codingStandardsChangeSetting Squiz.WhiteSpace.MemberVarSpacing spacingBeforeFirst 0
+
+class MyClass
+{
+ public $var1 = 'value';
+
+ public $var2 = 'value';
+
+ public $var3 = 'value';
+
+}//end class
+
+class MyClass
+{
+ public $var1 = 'value';
+
+ public $var2 = 'value';
+
+ public $var3 = 'value';
+
+}//end class
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.MemberVarSpacing spacing 0
+
+class MyClass
+{
+ public $var1 = 'value';
+ public $var2 = 'value';
+ public $var3 = 'value';
+
+}//end class
+
+interface MyInterface
+{
+ /* testing */
+ public $var1 = 'value';
+ public $var2 = 'value';
+ public $var3 = 'value';
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Unit test class for the MemberVarSpacing 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class MemberVarSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 4 => 1,
+ 7 => 1,
+ 20 => 1,
+ 30 => 1,
+ 35 => 1,
+ 44 => 1,
+ 50 => 1,
+ 73 => 1,
+ 86 => 1,
+ 106 => 1,
+ 115 => 1,
+ 150 => 1,
+ 160 => 1,
+ 165 => 1,
+ 177 => 1,
+ 186 => 1,
+ 200 => 1,
+ 209 => 1,
+ 211 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$this->testThis();
+$this-> testThis();
+$this -> testThis();
+$this->/* comment here */testThis();
+$this/* comment here */ -> testThis();
+$this
+ ->testThis();
+$this->
+ testThis();
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.ObjectOperatorSpacing ignoreNewlines true
+
+$this->testThis();
+$this-> testThis();
+$this -> testThis();
+$this->/* comment here */testThis();
+$this/* comment here */ -> testThis();
+$this
+ ->testThis();
+$this->
+ testThis();
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.ObjectOperatorSpacing ignoreNewlines false
+
+thisObject::testThis();
+thisObject:: testThis();
+thisObject :: testThis();
+thisObject::/* comment here */testThis();
+thisObject/* comment here */ :: testThis();
+thisObject
+ ::testThis();
+thisObject::
+ testThis();
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.ObjectOperatorSpacing ignoreNewlines true
+
+thisObject::testThis();
+thisObject:: testThis();
+thisObject :: testThis();
+thisObject::/* comment here */testThis();
+thisObject/* comment here */ :: testThis();
+thisObject
+ ::testThis();
+thisObject::
+ testThis();
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.ObjectOperatorSpacing ignoreNewlines false
--- /dev/null
+<?php
+$this->testThis();
+$this->testThis();
+$this->testThis();
+$this->/* comment here */testThis();
+$this/* comment here */->testThis();
+$this->testThis();
+$this->testThis();
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.ObjectOperatorSpacing ignoreNewlines true
+
+$this->testThis();
+$this->testThis();
+$this->testThis();
+$this->/* comment here */testThis();
+$this/* comment here */->testThis();
+$this
+ ->testThis();
+$this->
+ testThis();
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.ObjectOperatorSpacing ignoreNewlines false
+
+thisObject::testThis();
+thisObject::testThis();
+thisObject::testThis();
+thisObject::/* comment here */testThis();
+thisObject/* comment here */::testThis();
+thisObject::testThis();
+thisObject::testThis();
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.ObjectOperatorSpacing ignoreNewlines true
+
+thisObject::testThis();
+thisObject::testThis();
+thisObject::testThis();
+thisObject::/* comment here */testThis();
+thisObject/* comment here */::testThis();
+thisObject
+ ::testThis();
+thisObject::
+ testThis();
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.ObjectOperatorSpacing ignoreNewlines false
--- /dev/null
+<?php
+/**
+ * Unit test class for the ObjectOperatorSpacing 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ObjectOperatorSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 4 => 2,
+ 6 => 2,
+ 8 => 1,
+ 9 => 1,
+ 15 => 1,
+ 16 => 2,
+ 18 => 2,
+ 27 => 1,
+ 28 => 2,
+ 30 => 2,
+ 32 => 1,
+ 33 => 1,
+ 39 => 1,
+ 40 => 2,
+ 42 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+$result = 1 + 2;
+$result = 1 + 2;
+$result = 1 + 2;
+$result = 1 +2;
+$result = 1+ 2;
+$result = 1+2;
+
+$result = 1 - 2;
+$result = 1 - 2;
+$result = 1 - 2;
+$result = 1 -2;
+$result = 1- 2;
+$result = 1-2;
+
+$result = 1 * 2;
+$result = 1 * 2;
+$result = 1 * 2;
+$result = 1 *2;
+$result = 1* 2;
+$result = 1*2;
+
+$result = 1 / 2;
+$result = 1 / 2;
+$result = 1 / 2;
+$result = 1 /2;
+$result = 1/ 2;
+$result = 1/2;
+
+$result = 1 % 2;
+$result = 1 % 2;
+$result = 1 % 2;
+$result = 1 %2;
+$result = 1% 2;
+$result = 1%2;
+$result = '100%';
+
+$result += 4;
+$result+=4;
+$result -= 4;
+$result-=4;
+$result /= 4;
+$result/=4;
+$result *=4;
+$result*=4;
+
+$result =& $thing;
+if ($result & 4) {
+ if ($result | 4) {
+ }
+}
+if ($result&4 && $result & 4) {
+ if ($result |4 || $result | 4) {
+ }
+}
+
+$result = ((1 + 2) - (3 * 4 / 5) % 6);
+$result = ((1+ 2) - (3*4/5) % 6);
+return -1;
+$file = '...'.substr($file, (($padding * -1) + 3));
+
+$totalErrors += $errors['errors'];
+$totalWarnings += $errors['warnings'];
+
+if (substr($line, 0, 3) === '/**') {
+ $line = substr($line, 3);
+} else if (substr($line, -2, 2) === '*/') {
+ $line = substr(-2, 0, -2);
+} else if ($line{0} === '*') {
+ $line = substr($line, 1);
+}
+
+if ($pos === -1) {
+}
+
+for ($i=0; $i<=5; $i++) {
+ $j^= $i;
+ $k %=$i;
+ $l&=$i;
+ $m.= 'Hello ';
+}
+
+$z = ($a+ $b- $c* $d/ $e% $f^ $g);
+$z = ($a +$b -$c *$d /$e %$f ^$g);
+
+$a== $b && $c!= $d && $e=== $f && $g!== $h;
+$i> $j && $k< $l && $m>= $n && $o<= $p && $q<> $r;
+
+$a ==$b && $c !=$d && $e ===$f && $g !==$h;
+$i >$j && $k <$l && $m >=$n && $o <=$p && $q <>$r;
+
+function myFunction($variable=0, $var2='string') {}
+
+if (index > -1) {
+}
+
+array_walk_recursive($array, function(&$item) use (&$something) {
+});
+
+$var = saveFile(&$model, &$foo);
+
+// This is all valid.
+$boo = -$foo;
+function foo($boo = -1) {}
+$foo = array('boo' => -1);
+$x = $test ? -1 : 1;
+$y = $test ? 1 : -1;
+$z = $test ?: false;
+
+$closureWithDefaultParameter = function (array $testArray=array()) {};
+
+switch ($foo) {
+ case -1:
+ break;
+}
+
+$y = 1 * -1;
+$y = -1 * 1;
+$y = -1 * $var;
+$y = 10 / -2;
+$y = -10 / 2;
+$y = (-10 / 2);
+$y = (-10 / $var);
+$y = 10 + -2;
+$y = -10 + 2;
+
+$a = $x?$y:$z;
+$a = $x ? $y : $z;
+
+$y = 1
+ + 2
+ - 3;
+
+$y = 1 +
+ 2 -
+ 3;
+
+$y = 1
++ 2
+- 3;
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.OperatorSpacing ignoreNewlines true
+$y = 1
+ + 2
+ - 3;
+
+$y = 1 +
+ 2 -
+ 3;
+
+$y = 1
++ 2
+- 3;
+// @codingStandardsChangeSetting Squiz.WhiteSpace.OperatorSpacing ignoreNewlines false
+
+if (true || -1 == $b) {
+}
+
+$var = array(-1);
+$var = [-1];
+$var = [0, -1, -2];
+
+$y = array(&$x);
+$y = [&$x];
+$y = array(&$a, &$b, &$c);
+$y = [&$a, &$b, &$c];
+$y = array(&$a => 1, 2 => &$b, &$c);
+$y = [&$a => 1, 2 => &$b, &$c];
+
+if ($a <=> $b) {
+}
+
+if ($a <=>$b) {
+}
+
+$a |= $b;
+$a **= $b;
+$a ??= $b;
+
+$a = +1;
+
+function bar($boo = +1) {}
+
+$username = $_GET['user']??'nobody';
+
+function foo(string $bar, array $baz, ?MyClass $object) : MyClass {}
+
+declare(strict_types=1);
+
+function foo($c = ((BAR)?10:100)) {}
+
+$res = $a ?: $b;
+$res = $a ?: $b;
+$res = $a ?: $b;
+$res = $a ?: $b;
+
+$res = $a ? : $b;
+$res = $a ? : $b;
+$res = $a ? : $b;
+$res = $a ? : $b;
+
+function foo(string $a = '', ?string $b = ''): ?string {}
+
+// Issue #1605.
+$text = preg_replace_callback(
+ self::CHAR_REFS_REGEX,
+ [ 'Sanitizer', 'decodeCharReferencesCallback' ],
+ $text, /* limit */ -1, $count );
+
+if (true || /* test */ -1 == $b) {}
+$y = 10 + /* test */ -2;
+
+// Issue #1604.
+Hooks::run( 'ParserOptionsRegister', [
+ &self::$defaults,
+ &self::$inCacheKey,
+ &self::$lazyOptions,
+] );
--- /dev/null
+<?php
+
+$result = 1 + 2;
+$result = 1 + 2;
+$result = 1 + 2;
+$result = 1 + 2;
+$result = 1 + 2;
+$result = 1 + 2;
+
+$result = 1 - 2;
+$result = 1 - 2;
+$result = 1 - 2;
+$result = 1 - 2;
+$result = 1 - 2;
+$result = 1 - 2;
+
+$result = 1 * 2;
+$result = 1 * 2;
+$result = 1 * 2;
+$result = 1 * 2;
+$result = 1 * 2;
+$result = 1 * 2;
+
+$result = 1 / 2;
+$result = 1 / 2;
+$result = 1 / 2;
+$result = 1 / 2;
+$result = 1 / 2;
+$result = 1 / 2;
+
+$result = 1 % 2;
+$result = 1 % 2;
+$result = 1 % 2;
+$result = 1 % 2;
+$result = 1 % 2;
+$result = 1 % 2;
+$result = '100%';
+
+$result += 4;
+$result += 4;
+$result -= 4;
+$result -= 4;
+$result /= 4;
+$result /= 4;
+$result *= 4;
+$result *= 4;
+
+$result =& $thing;
+if ($result & 4) {
+ if ($result | 4) {
+ }
+}
+if ($result & 4 && $result & 4) {
+ if ($result | 4 || $result | 4) {
+ }
+}
+
+$result = ((1 + 2) - (3 * 4 / 5) % 6);
+$result = ((1 + 2) - (3 * 4 / 5) % 6);
+return -1;
+$file = '...'.substr($file, (($padding * -1) + 3));
+
+$totalErrors += $errors['errors'];
+$totalWarnings += $errors['warnings'];
+
+if (substr($line, 0, 3) === '/**') {
+ $line = substr($line, 3);
+} else if (substr($line, -2, 2) === '*/') {
+ $line = substr(-2, 0, -2);
+} else if ($line{0} === '*') {
+ $line = substr($line, 1);
+}
+
+if ($pos === -1) {
+}
+
+for ($i = 0; $i <= 5; $i++) {
+ $j ^= $i;
+ $k %= $i;
+ $l &= $i;
+ $m .= 'Hello ';
+}
+
+$z = ($a + $b - $c * $d / $e % $f ^ $g);
+$z = ($a + $b - $c * $d / $e % $f ^ $g);
+
+$a == $b && $c != $d && $e === $f && $g !== $h;
+$i > $j && $k < $l && $m >= $n && $o <= $p && $q <> $r;
+
+$a == $b && $c != $d && $e === $f && $g !== $h;
+$i > $j && $k < $l && $m >= $n && $o <= $p && $q <> $r;
+
+function myFunction($variable=0, $var2='string') {}
+
+if (index > -1) {
+}
+
+array_walk_recursive($array, function(&$item) use (&$something) {
+});
+
+$var = saveFile(&$model, &$foo);
+
+// This is all valid.
+$boo = -$foo;
+function foo($boo = -1) {}
+$foo = array('boo' => -1);
+$x = $test ? -1 : 1;
+$y = $test ? 1 : -1;
+$z = $test ?: false;
+
+$closureWithDefaultParameter = function (array $testArray=array()) {};
+
+switch ($foo) {
+ case -1:
+ break;
+}
+
+$y = 1 * -1;
+$y = -1 * 1;
+$y = -1 * $var;
+$y = 10 / -2;
+$y = -10 / 2;
+$y = (-10 / 2);
+$y = (-10 / $var);
+$y = 10 + -2;
+$y = -10 + 2;
+
+$a = $x ? $y : $z;
+$a = $x ? $y : $z;
+
+$y = 1 + 2 - 3;
+
+$y = 1 + 2 - 3;
+
+$y = 1 + 2 - 3;
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.OperatorSpacing ignoreNewlines true
+$y = 1
+ + 2
+ - 3;
+
+$y = 1 +
+ 2 -
+ 3;
+
+$y = 1
++ 2
+- 3;
+// @codingStandardsChangeSetting Squiz.WhiteSpace.OperatorSpacing ignoreNewlines false
+
+if (true || -1 == $b) {
+}
+
+$var = array(-1);
+$var = [-1];
+$var = [0, -1, -2];
+
+$y = array(&$x);
+$y = [&$x];
+$y = array(&$a, &$b, &$c);
+$y = [&$a, &$b, &$c];
+$y = array(&$a => 1, 2 => &$b, &$c);
+$y = [&$a => 1, 2 => &$b, &$c];
+
+if ($a <=> $b) {
+}
+
+if ($a <=> $b) {
+}
+
+$a |= $b;
+$a **= $b;
+$a ??= $b;
+
+$a = +1;
+
+function bar($boo = +1) {}
+
+$username = $_GET['user'] ?? 'nobody';
+
+function foo(string $bar, array $baz, ?MyClass $object) : MyClass {}
+
+declare(strict_types=1);
+
+function foo($c = ((BAR) ? 10 : 100)) {}
+
+$res = $a ?: $b;
+$res = $a ?: $b;
+$res = $a ?: $b;
+$res = $a ?: $b;
+
+$res = $a ? : $b;
+$res = $a ? : $b;
+$res = $a ? : $b;
+$res = $a ? : $b;
+
+function foo(string $a = '', ?string $b = ''): ?string {}
+
+// Issue #1605.
+$text = preg_replace_callback(
+ self::CHAR_REFS_REGEX,
+ [ 'Sanitizer', 'decodeCharReferencesCallback' ],
+ $text, /* limit */ -1, $count );
+
+if (true || /* test */ -1 == $b) {}
+$y = 10 + /* test */ -2;
+
+// Issue #1604.
+Hooks::run( 'ParserOptionsRegister', [
+ &self::$defaults,
+ &self::$inCacheKey,
+ &self::$lazyOptions,
+] );
--- /dev/null
+
+
+result = 1 + 2;
+result = 1 + 2;
+result = 1 + 2;
+result = 1 +2;
+result = 1+ 2;
+result = 1+2;
+
+result = 1 - 2;
+result = 1 - 2;
+result = 1 - 2;
+result = 1 -2;
+result = 1- 2;
+result = 1-2;
+
+result = 1 * 2;
+result = 1 * 2;
+result = 1 * 2;
+result = 1 *2;
+result = 1* 2;
+result = 1*2;
+
+result = 1 / 2;
+result = 1 / 2;
+result = 1 / 2;
+result = 1 /2;
+result = 1/ 2;
+result = 1/2;
+
+result = 1 % 2;
+result = 1 % 2;
+result = 1 % 2;
+result = 1 %2;
+result = 1% 2;
+result = 1%2;
+result = '100%';
+
+result += 4;
+result+=4;
+result -= 4;
+result-=4;
+result /= 4;
+result/=4;
+result *=4;
+result*=4;
+
+$.localScroll({offset: {top: -32}});
+
+switch (result) {
+ case -1:
+ break;
+}
+
+result = x?y:z;
+result = x ? y : z;
+
+if (something === true
+ ^ somethingElse === true
+) {
+ return false;
+}
+
+y = 1
+ + 2
+ - 3;
+
+y = 1 +
+ 2 -
+ 3;
+
+y = 1
++ 2
+- 3;
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.OperatorSpacing ignoreNewlines true
+y = 1
+ + 2
+ - 3;
+
+y = 1 +
+ 2 -
+ 3;
+
+y = 1
++ 2
+- 3;
+// @codingStandardsChangeSetting Squiz.WhiteSpace.OperatorSpacing ignoreNewlines false
+
+if (true || -1 == b) {
+}
+
+x = x << y;
+x <<= y;
+x = x >> y;
+x >>= y;
+x = x >>> y;
+x >>>= y;
+
+var foo = bar.map(baz=> baz.length);
--- /dev/null
+
+
+result = 1 + 2;
+result = 1 + 2;
+result = 1 + 2;
+result = 1 + 2;
+result = 1 + 2;
+result = 1 + 2;
+
+result = 1 - 2;
+result = 1 - 2;
+result = 1 - 2;
+result = 1 - 2;
+result = 1 - 2;
+result = 1 - 2;
+
+result = 1 * 2;
+result = 1 * 2;
+result = 1 * 2;
+result = 1 * 2;
+result = 1 * 2;
+result = 1 * 2;
+
+result = 1 / 2;
+result = 1 / 2;
+result = 1 / 2;
+result = 1 / 2;
+result = 1 / 2;
+result = 1 / 2;
+
+result = 1 % 2;
+result = 1 % 2;
+result = 1 % 2;
+result = 1 % 2;
+result = 1 % 2;
+result = 1 % 2;
+result = '100%';
+
+result += 4;
+result += 4;
+result -= 4;
+result -= 4;
+result /= 4;
+result /= 4;
+result *= 4;
+result *= 4;
+
+$.localScroll({offset: {top: -32}});
+
+switch (result) {
+ case -1:
+ break;
+}
+
+result = x ? y : z;
+result = x ? y : z;
+
+if (something === true
+ ^ somethingElse === true
+) {
+ return false;
+}
+
+y = 1 + 2 - 3;
+
+y = 1 + 2 - 3;
+
+y = 1 + 2 - 3;
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.OperatorSpacing ignoreNewlines true
+y = 1
+ + 2
+ - 3;
+
+y = 1 +
+ 2 -
+ 3;
+
+y = 1
++ 2
+- 3;
+// @codingStandardsChangeSetting Squiz.WhiteSpace.OperatorSpacing ignoreNewlines false
+
+if (true || -1 == b) {
+}
+
+x = x << y;
+x <<= y;
+x = x >> y;
+x >>= y;
+x = x >>> y;
+x >>>= y;
+
+var foo = bar.map(baz => baz.length);
--- /dev/null
+<?php
+/**
+ * Unit test class for the OperatorSpacing 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class OperatorSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='OperatorSpacingUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'OperatorSpacingUnitTest.inc':
+ return array(
+ 4 => 1,
+ 5 => 2,
+ 6 => 1,
+ 7 => 1,
+ 8 => 2,
+ 11 => 1,
+ 12 => 2,
+ 13 => 1,
+ 14 => 1,
+ 15 => 2,
+ 18 => 1,
+ 19 => 2,
+ 20 => 1,
+ 21 => 1,
+ 22 => 2,
+ 25 => 1,
+ 26 => 2,
+ 27 => 1,
+ 28 => 1,
+ 29 => 2,
+ 32 => 1,
+ 33 => 2,
+ 34 => 1,
+ 35 => 1,
+ 36 => 2,
+ 40 => 2,
+ 42 => 2,
+ 44 => 2,
+ 45 => 1,
+ 46 => 2,
+ 53 => 4,
+ 54 => 3,
+ 59 => 10,
+ 64 => 1,
+ 77 => 4,
+ 78 => 1,
+ 79 => 1,
+ 80 => 2,
+ 81 => 1,
+ 84 => 6,
+ 85 => 6,
+ 87 => 4,
+ 88 => 5,
+ 90 => 4,
+ 91 => 5,
+ 128 => 4,
+ 132 => 1,
+ 133 => 1,
+ 135 => 1,
+ 136 => 1,
+ 140 => 1,
+ 141 => 1,
+ 174 => 1,
+ 177 => 1,
+ 178 => 1,
+ 179 => 1,
+ 185 => 2,
+ 191 => 4,
+ 194 => 1,
+ 195 => 1,
+ 196 => 2,
+ 199 => 1,
+ 200 => 1,
+ 201 => 2,
+ );
+ break;
+ case 'OperatorSpacingUnitTest.js':
+ return array(
+ 4 => 1,
+ 5 => 2,
+ 6 => 1,
+ 7 => 1,
+ 8 => 2,
+ 11 => 1,
+ 12 => 2,
+ 13 => 1,
+ 14 => 1,
+ 15 => 2,
+ 18 => 1,
+ 19 => 2,
+ 20 => 1,
+ 21 => 1,
+ 22 => 2,
+ 25 => 1,
+ 26 => 2,
+ 27 => 1,
+ 28 => 1,
+ 29 => 2,
+ 32 => 1,
+ 33 => 2,
+ 34 => 1,
+ 35 => 1,
+ 36 => 2,
+ 40 => 2,
+ 42 => 2,
+ 44 => 2,
+ 45 => 1,
+ 46 => 2,
+ 55 => 4,
+ 65 => 1,
+ 66 => 1,
+ 68 => 1,
+ 69 => 1,
+ 73 => 1,
+ 74 => 1,
+ 100 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+var x = {
+ b: 'x',
+ xasd: x,
+ abc:x,
+ a: function () {
+ alert('thats right');
+ x = (x?a:x);
+ },
+ casdasd : 123123,
+ omgwtfbbq: {
+ a: 1,
+ b: 2
+ }
+};
+
+id = id.replace(/row\/:/gi, '');
+
+outer_loop:
+for (i=0; i<3; i++) {
+ for (j=0; j<5; j++) {
+ if (j==x)
+ break outer_loop;
+ }
+}
+alert('hi');
+
+even_number: if ((i % 2) == 0) {
+ if (i == 12)
+ break even_number;
+}
+
+switch (blah) {
+ case dfx.DOM_VK_LEFT:
+ this.caretLeft(shiftKey);
+ break;
+ default:
+ if (blah) {
+ }
+ break;
+}
--- /dev/null
+var x = {
+ b: 'x',
+ xasd: x,
+ abc: x,
+ a: function () {
+ alert('thats right');
+ x = (x?a:x);
+ },
+ casdasd: 123123,
+ omgwtfbbq: {
+ a: 1,
+ b: 2
+ }
+};
+
+id = id.replace(/row\/:/gi, '');
+
+outer_loop: for (i=0; i<3; i++) {
+ for (j=0; j<5; j++) {
+ if (j==x)
+ break outer_loop;
+ }
+}
+alert('hi');
+
+even_number: if ((i % 2) == 0) {
+ if (i == 12)
+ break even_number;
+}
+
+switch (blah) {
+ case dfx.DOM_VK_LEFT:
+ this.caretLeft(shiftKey);
+ break;
+ default:
+ if (blah) {
+ }
+ break;
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the PropertyLabel 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class PropertyLabelSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 2 => 1,
+ 4 => 1,
+ 9 => 2,
+ 10 => 1,
+ 12 => 1,
+ 18 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+use some\foo\{ClassA, ClassB, ClassC as C};
+class Test
+{
+ public function __construct()
+ {
+ }
+
+ function test1()
+ {
+ }
+
+ function test2() {}
+
+ private function _test3()
+ {
+ }
+
+}
+
+
+class Test2
+{
+ }
+
+
+public function test2()
+{
+ if ($str{0}) {
+ $chr = $str{0};
+ }
+
+ if (!class_exists($class_name)) {
+ echo $error;
+ }
+ $this->{$property} =& new $class_name($this->db_index);
+ $this->modules[$module] =& $this->{$property};
+}
+
+foreach ($elements as $element) {
+ if ($something) {
+ // Do IF.
+ } else if ($somethingElse) {
+ // Do ELSE.
+ }
+}
+
+if ($token['code'] === T_COMMENT
+ && $multiLineComment === false
+ && (substr($token['content'], 0, 2) === '//'
+ || $token['content']{0} === '#')
+) {
+}
+
+switch ($blah) {
+ case 'one':
+ echo 'one';
+ break;
+ default:
+ echo 'another';
+}
+
+?>
+
+<?php while($row = $data->getRow()): ?>
+ <p><?= $val ?></p>
+<?php endwhile; ?>
+
+<body>
+ <div>
+ <?php if (true == false) : ?>
+ <h1>o hai!</h1>
+ <?php endif; ?>
+ </div>
+</body>
+
+<?php
+ if ($foo) {
+ ?>
+ <?php } ?>
+
+<?php
+ if ($foo) {
+ ?>
+ <?php } ?>
+
+<?php
+ if ($foo) {
+ ?>
+ <?php } ?>
+
+<?php
+public function foo()
+{
+ $foo('some
+ long description', function () {
+ });
+
+ $foo('some
+ long
+ description', function () {
+});
+
+ $foo(
+'bad indent here leads to bad indent for closer', function () {
+});
+}
--- /dev/null
+<?php
+use some\foo\{ClassA, ClassB, ClassC as C};
+class Test
+{
+ public function __construct()
+ {
+ }
+
+ function test1()
+ {
+ }
+
+ function test2() {
+ }
+
+ private function _test3()
+ {
+ }
+
+}
+
+
+class Test2
+{
+}
+
+
+public function test2()
+{
+ if ($str{0}) {
+ $chr = $str{0};
+ }
+
+ if (!class_exists($class_name)) {
+ echo $error;
+ }
+ $this->{$property} =& new $class_name($this->db_index);
+ $this->modules[$module] =& $this->{$property};
+}
+
+foreach ($elements as $element) {
+ if ($something) {
+ // Do IF.
+ } else if ($somethingElse) {
+ // Do ELSE.
+ }
+}
+
+if ($token['code'] === T_COMMENT
+ && $multiLineComment === false
+ && (substr($token['content'], 0, 2) === '//'
+ || $token['content']{0} === '#')
+) {
+}
+
+switch ($blah) {
+ case 'one':
+ echo 'one';
+ break;
+ default:
+ echo 'another';
+}
+
+?>
+
+<?php while($row = $data->getRow()): ?>
+ <p><?= $val ?></p>
+<?php endwhile; ?>
+
+<body>
+ <div>
+ <?php if (true == false) : ?>
+ <h1>o hai!</h1>
+ <?php endif; ?>
+ </div>
+</body>
+
+<?php
+ if ($foo) {
+ ?>
+ <?php } ?>
+
+<?php
+ if ($foo) {
+ ?>
+ <?php } ?>
+
+<?php
+ if ($foo) {
+ ?>
+ <?php } ?>
+
+<?php
+public function foo()
+{
+ $foo('some
+ long description', function () {
+ });
+
+ $foo('some
+ long
+ description', function () {
+ });
+
+ $foo(
+'bad indent here leads to bad indent for closer', function () {
+});
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ScopeClosingBrace 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ScopeClosingBraceUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 11 => 1,
+ 13 => 1,
+ 24 => 1,
+ 80 => 1,
+ 102 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+class MyClass
+{
+ public static $var = null;
+ protected $var = null;
+
+ public static $var = null;
+ protected $var = null;
+
+ private function myFunction() {}
+ public static function myFunction() {}
+
+ private function myFunction() {}
+ public static function myFunction() {}
+ private static function myFunction() {}
+
+ private static
+ function myFunction() {}
+
+ public static function output()
+ {
+ // New in PHP 5.3
+ static::bar();
+ }
+}
+
+abstract class Foo
+{
+ public static function getInstance()
+ {
+ return new static();
+ }
+}
+
+if ($geometry instanceof static) {
+ echo 'foo';
+}
+
+class MyClass1 {
+ use HelloWorld { sayHello as private; }
+}
--- /dev/null
+<?php
+/**
+ * Unit test class for the ScopeKeywordSpacing 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ScopeKeywordSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 7 => 2,
+ 8 => 1,
+ 13 => 1,
+ 14 => 1,
+ 15 => 1,
+ 17 => 2,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$test = $this->testThis();
+$test = $this->testThis() ;
+$test = $this->testThis() ;
+for ($var = 1 ; $var < 10 ; $var++) {
+ echo $var ;
+}
+$test = $this->testThis() /* comment here */;
+$test = $this->testThis() /* comment here */ ;
+
+$hello ='foo';
+;
+
+$sum = $a /* + $b */;
+$sum = $a // + $b
+;
+$sum = $a /* + $b
+ + $c */ ;
--- /dev/null
+<?php
+$test = $this->testThis();
+$test = $this->testThis();
+$test = $this->testThis();
+for ($var = 1; $var < 10; $var++) {
+ echo $var;
+}
+$test = $this->testThis(); /* comment here */
+$test = $this->testThis(); /* comment here */
+
+$hello ='foo';
+;
+
+$sum = $a; /* + $b */
+$sum = $a; // + $b
+
+$sum = $a; /* + $b
+ + $c */
--- /dev/null
+var x = {
+ a: function () {
+ alert('thats right') ;
+ x = (x?a:x) ;
+ },
+} ;
+
+id = id.replace(/row\/:;/gi, '');
+
+for (i=0 ; i<3 ; i++) {
+ for (j=0; j<5 ; j++) {
+ if (j==x)
+ break ;
+ }
+}
+alert('hi');
+;
+
+var sum = a /* + b */;
+
+var sum = a // +b
+;
+
+var sum = a /* +b
+ + c */ ;
--- /dev/null
+var x = {
+ a: function () {
+ alert('thats right');
+ x = (x?a:x);
+ },
+};
+
+id = id.replace(/row\/:;/gi, '');
+
+for (i=0; i<3; i++) {
+ for (j=0; j<5; j++) {
+ if (j==x)
+ break;
+ }
+}
+alert('hi');
+;
+
+var sum = a; /* + b */
+
+var sum = a; // +b
+
+
+var sum = a; /* +b
+ + c */
--- /dev/null
+<?php
+/**
+ * Unit test class for the SemicolonSpacing 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class SemicolonSpacingUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='SemicolonSpacingUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'SemicolonSpacingUnitTest.inc':
+ return array(
+ 3 => 1,
+ 4 => 1,
+ 5 => 2,
+ 6 => 1,
+ 8 => 1,
+ 9 => 1,
+ 14 => 1,
+ 16 => 1,
+ 18 => 1,
+ );
+ break;
+ case 'SemicolonSpacingUnitTest.js':
+ return array(
+ 3 => 1,
+ 4 => 1,
+ 6 => 1,
+ 10 => 2,
+ 11 => 1,
+ 13 => 1,
+ 19 => 1,
+ 22 => 1,
+ 25 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
+
+.HelpWidgetType-new-bug-title{
+ float: left;
+}
+
+/* @codingStandardsChangeSetting Squiz.WhiteSpace.SuperfluousWhitespace ignoreBlankLines true */
+.HelpWidgetType-new-bug-title{
+ float: left;
+}
+
+.HelpWidgetType-new-bug-title{
+ float: left;
+}
+/* @codingStandardsChangeSetting Squiz.WhiteSpace.SuperfluousWhitespace ignoreBlankLines false */
+
--- /dev/null
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
+
+.HelpWidgetType-new-bug-title{
+ float: left;
+}
+
+/* @codingStandardsChangeSetting Squiz.WhiteSpace.SuperfluousWhitespace ignoreBlankLines true */
+.HelpWidgetType-new-bug-title{
+ float: left;
+}
+
+.HelpWidgetType-new-bug-title{
+ float: left;
+}
+/* @codingStandardsChangeSetting Squiz.WhiteSpace.SuperfluousWhitespace ignoreBlankLines false */
--- /dev/null
+
+alert('hi');
+alert('hello');
+
+if (something) {
+
+}
+
+
+function myFunction()
+{
+ alert('code here');
+
+ alert('code here');
+
+
+ // Hello.
+
+ /*
+ HI
+ */
+
+
+}
+
+function myFunction2()
+{
+ alert('code here');
+
+
+ alert('code here');
+
+}
+
+MyFunction = function()
+{
+ alert('code here');
+
+
+ alert('code here');
+
+};
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.SuperfluousWhitespace ignoreBlankLines true
+
+function myFunction2()
+{
+ alert('code here');
+
+ alert('code here');
+
+}
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.SuperfluousWhitespace ignoreBlankLines false
+
+
--- /dev/null
+alert('hi');
+alert('hello');
+
+if (something) {
+
+}
+
+
+function myFunction()
+{
+ alert('code here');
+
+ alert('code here');
+
+ // Hello.
+
+ /*
+ HI
+ */
+
+}
+
+function myFunction2()
+{
+ alert('code here');
+
+ alert('code here');
+
+}
+
+MyFunction = function()
+{
+ alert('code here');
+
+ alert('code here');
+
+};
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.SuperfluousWhitespace ignoreBlankLines true
+
+function myFunction2()
+{
+ alert('code here');
+
+ alert('code here');
+
+}
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.SuperfluousWhitespace ignoreBlankLines false
+
--- /dev/null
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
\ No newline at end of file
--- /dev/null
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
\ No newline at end of file
--- /dev/null
+alert('hi');
\ No newline at end of file
--- /dev/null
+alert('hi');
\ No newline at end of file
--- /dev/null
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
--- /dev/null
+.HelpWidgetType-new-bug-title {
+ float: left;
+}
\ No newline at end of file
--- /dev/null
+alert('hi');
--- /dev/null
+alert('hi');
--- /dev/null
+
+<?php
+echo 'hi';
+echo 'hello';
+
+if ($something) {
+
+}
+
+
+function myFunction()
+{
+ echo 'code here';
+
+ echo 'code here';
+
+
+ // Hello.
+
+ /*
+ HI
+ */
+
+
+}
+
+/**
+ * Doc comment with a white space at the end.
+ */
+function myFunction2()
+{
+ echo 'code here';
+
+
+ echo 'code here';
+
+}
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.SuperfluousWhitespace ignoreBlankLines true
+
+function myFunction2()
+{
+ echo 'code here';
+
+ echo 'code here';
+
+}
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.SuperfluousWhitespace ignoreBlankLines false
+
+// Уберём из системных свойств все кроме информации об услугах
+
+?>
+
--- /dev/null
+<?php
+echo 'hi';
+echo 'hello';
+
+if ($something) {
+
+}
+
+
+function myFunction()
+{
+ echo 'code here';
+
+ echo 'code here';
+
+ // Hello.
+
+ /*
+ HI
+ */
+
+}
+
+/**
+ * Doc comment with a white space at the end.
+ */
+function myFunction2()
+{
+ echo 'code here';
+
+ echo 'code here';
+
+}
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.SuperfluousWhitespace ignoreBlankLines true
+
+function myFunction2()
+{
+ echo 'code here';
+
+ echo 'code here';
+
+}
+
+// @codingStandardsChangeSetting Squiz.WhiteSpace.SuperfluousWhitespace ignoreBlankLines false
+
+// Уберём из системных свойств все кроме информации об услугах
+
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the SuperfluousWhitespace 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\Standards\Squiz\Tests\WhiteSpace;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class SuperfluousWhitespaceUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='SuperfluousWhitespaceUnitTest.inc')
+ {
+ switch ($testFile) {
+ case 'SuperfluousWhitespaceUnitTest.inc':
+ return array(
+ 2 => 1,
+ 4 => 1,
+ 5 => 1,
+ 6 => 1,
+ 7 => 1,
+ 16 => 1,
+ 23 => 1,
+ 28 => 1,
+ 33 => 1,
+ 53 => 1,
+ );
+ break;
+ case 'SuperfluousWhitespaceUnitTest.1.js':
+ return array(
+ 1 => 1,
+ 3 => 1,
+ 4 => 1,
+ 5 => 1,
+ 6 => 1,
+ 15 => 1,
+ 22 => 1,
+ 29 => 1,
+ 38 => 1,
+ 56 => 1,
+ );
+ break;
+ case 'SuperfluousWhitespaceUnitTest.1.css':
+ return array(
+ 1 => 1,
+ 8 => 1,
+ 9 => 1,
+ 11 => 1,
+ 25 => 1,
+ );
+ break;
+ default:
+ return array();
+ break;
+ }//end switch
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?xml version="1.0"?>
+<ruleset name="Squiz">
+ <description>The Squiz coding standard.</description>
+
+ <!-- Include some specific sniffs -->
+ <rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
+ <rule ref="Generic.CodeAnalysis.EmptyStatement"/>
+ <rule ref="Generic.Commenting.Todo"/>
+ <rule ref="Generic.Commenting.DocComment"/>
+ <rule ref="Generic.ControlStructures.InlineControlStructure"/>
+ <rule ref="Generic.Formatting.DisallowMultipleStatements"/>
+ <rule ref="Generic.Formatting.SpaceAfterCast"/>
+ <rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
+ <rule ref="Generic.NamingConventions.ConstructorName"/>
+ <rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
+ <rule ref="Generic.PHP.DeprecatedFunctions"/>
+ <rule ref="Generic.PHP.DisallowShortOpenTag"/>
+ <rule ref="Generic.PHP.LowerCaseKeyword"/>
+ <rule ref="Generic.PHP.LowerCaseConstant"/>
+ <rule ref="Generic.Strings.UnnecessaryStringConcat"/>
+ <rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
+ <rule ref="Generic.WhiteSpace.ScopeIndent"/>
+ <rule ref="PEAR.ControlStructures.MultiLineCondition"/>
+ <rule ref="PEAR.Files.IncludingFile"/>
+ <rule ref="PEAR.Formatting.MultiLineAssignment"/>
+ <rule ref="PEAR.Functions.ValidDefaultValue"/>
+ <rule ref="PSR2.Files.EndFileNewline"/>
+ <rule ref="Zend.Files.ClosingTag"/>
+ <rule ref="Zend.Debug.CodeAnalyzer"/>
+
+ <!-- Lines can be 120 chars long, but never show errors -->
+ <rule ref="Generic.Files.LineLength">
+ <properties>
+ <property name="lineLimit" value="120"/>
+ <property name="absoluteLineLimit" value="0"/>
+ </properties>
+ </rule>
+
+ <!-- Use Unix newlines -->
+ <rule ref="Generic.Files.LineEndings">
+ <properties>
+ <property name="eolChar" value="\n"/>
+ </properties>
+ </rule>
+
+ <!-- Have 20 chars padding maximum and always show as errors -->
+ <rule ref="Generic.Formatting.MultipleStatementAlignment">
+ <properties>
+ <property name="maxPadding" value="20"/>
+ <property name="error" value="true"/>
+ </properties>
+ </rule>
+
+ <!-- We allow empty catch statements -->
+ <rule ref="Generic.CodeAnalysis.EmptyStatement.DetectedCATCH">
+ <severity>0</severity>
+ </rule>
+
+ <!-- We don't want gsjlint throwing errors for things we already check -->
+ <rule ref="Generic.Debug.ClosureLinter">
+ <properties>
+ <property name="errorCodes" type="array" value="0210"/>
+ <property name="ignoreCodes" type="array" value="0001,0110,0240"/>
+ </properties>
+ </rule>
+ <rule ref="Generic.Debug.ClosureLinter.ExternalToolError">
+ <message>%2$s</message>
+ </rule>
+
+ <!-- Only one argument per line in multi-line function calls -->
+ <rule ref="PEAR.Functions.FunctionCallSignature">
+ <properties>
+ <property name="allowMultipleArguments" value="false"/>
+ </properties>
+ </rule>
+
+</ruleset>
--- /dev/null
+<documentation title="Zend Code Analyzer">
+ <standard>
+ <![CDATA[
+ PHP Code should pass the zend code analyzer.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: Valid PHP Code.">
+ <![CDATA[
+function foo($bar, $baz)
+{
+ return <em>$bar + $baz</em>;
+}
+ ]]>
+ </code>
+ <code title="Invalid: There is an unused function parameter.">
+ <![CDATA[
+function foo($bar, $baz)
+{
+ return <em>$bar + 2</em>;
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Closing PHP Tags">
+ <standard>
+ <![CDATA[
+ Files should not have closing php tags.
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: No closing tag at the end of the file.">
+ <![CDATA[
+<?php
+$var = 1;
+ ]]>
+ </code>
+ <code title="Invalid: A closing php tag is included at the end of the file.">
+ <![CDATA[
+<?php
+$var = 1;
+<em>?></em>
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<documentation title="Variable Names">
+ <standard>
+ <![CDATA[
+ Variable names should be camelCased with the first letter lowercase. Private and protected member variables should begin with an underscore
+ ]]>
+ </standard>
+ <code_comparison>
+ <code title="Valid: A multi-word variable uses camel casing.">
+ <![CDATA[
+<em>$testNumber</em> = 1;
+ ]]>
+ </code>
+ <code title="Invalid: A multi-word variable uses underscores and initial capitalization.">
+ <![CDATA[
+<em>$Test_Number</em> = 1;
+ ]]>
+ </code>
+ </code_comparison>
+ <code_comparison>
+ <code title="Valid: A private member variable begins with an underscore.">
+ <![CDATA[
+class Foo
+{
+ private $<em>_</em>bar;
+}
+ ]]>
+ </code>
+ <code title="Invalid: A private member variable does not begin with an underscore.">
+ <![CDATA[
+class Foo
+{
+ private $bar;
+}
+ ]]>
+ </code>
+ </code_comparison>
+</documentation>
--- /dev/null
+<?php
+/**
+ * Runs the Zend Code Analyzer (from Zend Studio) on the file.
+ *
+ * @author Holger Kral <holger.kral@zend.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\Standards\Zend\Sniffs\Debug;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+
+class CodeAnalyzerSniff implements Sniff
+{
+
+
+ /**
+ * Returns the token types that this sniff is interested in.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes the tokens that this sniff is interested in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
+ * @param int $stackPtr The position in the stack where
+ * the token was found.
+ *
+ * @return int
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $analyzerPath = Config::getExecutablePath('zend_ca');
+ if (is_null($analyzerPath) === true) {
+ return;
+ }
+
+ $fileName = $phpcsFile->getFilename();
+
+ // In the command, 2>&1 is important because the code analyzer sends its
+ // findings to stderr. $output normally contains only stdout, so using 2>&1
+ // will pipe even stderr to stdout.
+ $cmd = escapeshellcmd($analyzerPath).' '.escapeshellarg($fileName).' 2>&1';
+
+ // There is the possibility to pass "--ide" as an option to the analyzer.
+ // This would result in an output format which would be easier to parse.
+ // The problem here is that no cleartext error messages are returnwd; only
+ // error-code-labels. So for a start we go for cleartext output.
+ $exitCode = exec($cmd, $output, $retval);
+
+ // Variable $exitCode is the last line of $output if no error occures, on
+ // error it is numeric. Try to handle various error conditions and
+ // provide useful error reporting.
+ if (is_numeric($exitCode) === true && $exitCode > 0) {
+ if (is_array($output) === true) {
+ $msg = join('\n', $output);
+ }
+
+ throw new RuntimeException("Failed invoking ZendCodeAnalyzer, exitcode was [$exitCode], retval was [$retval], output was [$msg]");
+ }
+
+ if (is_array($output) === true) {
+ foreach ($output as $finding) {
+ // The first two lines of analyzer output contain
+ // something like this:
+ // > Zend Code Analyzer 1.2.2
+ // > Analyzing <filename>...
+ // So skip these...
+ $res = preg_match("/^.+\(line ([0-9]+)\):(.+)$/", $finding, $regs);
+ if (empty($regs) === true || $res === false) {
+ continue;
+ }
+
+ $phpcsFile->addWarningOnLine(trim($regs[2]), $regs[1], 'ExternalTool');
+ }
+ }
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks that the file does not end with a closing tag.
+ *
+ * @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\Standards\Zend\Sniffs\Files;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+
+class ClosingTagSniff implements Sniff
+{
+
+
+ /**
+ * Returns an array of tokens this test wants to listen for.
+ *
+ * @return array
+ */
+ public function register()
+ {
+ return array(T_OPEN_TAG);
+
+ }//end register()
+
+
+ /**
+ * Processes this sniff, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in
+ * the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ // Find the last non-empty token.
+ $tokens = $phpcsFile->getTokens();
+ for ($last = ($phpcsFile->numTokens - 1); $last > 0; $last--) {
+ if (trim($tokens[$last]['content']) !== '') {
+ break;
+ }
+ }
+
+ if ($tokens[$last]['code'] === T_CLOSE_TAG) {
+ $error = 'A closing tag is not permitted at the end of a PHP file';
+ $fix = $phpcsFile->addFixableError($error, $last, 'NotAllowed');
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken($last, '');
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'PHP closing tag at EOF', 'yes');
+ } else {
+ $phpcsFile->recordMetric($stackPtr, 'PHP closing tag at EOF', 'no');
+ }
+
+ // Ignore the rest of the file.
+ return ($phpcsFile->numTokens + 1);
+
+ }//end process()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Checks the naming of variables and member variables.
+ *
+ * @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\Standards\Zend\Sniffs\NamingConventions;
+
+use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
+use PHP_CodeSniffer\Util\Common;
+use PHP_CodeSniffer\Files\File;
+
+class ValidVariableNameSniff extends AbstractVariableSniff
+{
+
+ /**
+ * Tokens to ignore so that we can find a DOUBLE_COLON.
+ *
+ * @var array
+ */
+ private $ignore = array(
+ T_WHITESPACE,
+ T_COMMENT,
+ );
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ protected function processVariable(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $varName = ltrim($tokens[$stackPtr]['content'], '$');
+
+ $phpReservedVars = array(
+ '_SERVER' => true,
+ '_GET' => true,
+ '_POST' => true,
+ '_REQUEST' => true,
+ '_SESSION' => true,
+ '_ENV' => true,
+ '_COOKIE' => true,
+ '_FILES' => true,
+ 'GLOBALS' => true,
+ 'http_response_header' => true,
+ 'HTTP_RAW_POST_DATA' => true,
+ 'php_errormsg' => true,
+ );
+
+ // If it's a php reserved var, then its ok.
+ if (isset($phpReservedVars[$varName]) === true) {
+ return;
+ }
+
+ $objOperator = $phpcsFile->findNext(array(T_WHITESPACE), ($stackPtr + 1), null, true);
+ if ($tokens[$objOperator]['code'] === T_OBJECT_OPERATOR) {
+ // Check to see if we are using a variable from an object.
+ $var = $phpcsFile->findNext(array(T_WHITESPACE), ($objOperator + 1), null, true);
+ if ($tokens[$var]['code'] === T_STRING) {
+ // Either a var name or a function call, so check for bracket.
+ $bracket = $phpcsFile->findNext(array(T_WHITESPACE), ($var + 1), null, true);
+
+ if ($tokens[$bracket]['code'] !== T_OPEN_PARENTHESIS) {
+ $objVarName = $tokens[$var]['content'];
+
+ // There is no way for us to know if the var is public or private,
+ // so we have to ignore a leading underscore if there is one and just
+ // check the main part of the variable name.
+ $originalVarName = $objVarName;
+ if (substr($objVarName, 0, 1) === '_') {
+ $objVarName = substr($objVarName, 1);
+ }
+
+ if (Common::isCamelCaps($objVarName, false, true, false) === false) {
+ $error = 'Variable "%s" is not in valid camel caps format';
+ $data = array($originalVarName);
+ $phpcsFile->addError($error, $var, 'NotCamelCaps', $data);
+ } else if (preg_match('|\d|', $objVarName) === 1) {
+ $warning = 'Variable "%s" contains numbers but this is discouraged';
+ $data = array($originalVarName);
+ $phpcsFile->addWarning($warning, $stackPtr, 'ContainsNumbers', $data);
+ }
+ }//end if
+ }//end if
+ }//end if
+
+ // There is no way for us to know if the var is public or private,
+ // so we have to ignore a leading underscore if there is one and just
+ // check the main part of the variable name.
+ $originalVarName = $varName;
+ if (substr($varName, 0, 1) === '_') {
+ $objOperator = $phpcsFile->findPrevious(array(T_WHITESPACE), ($stackPtr - 1), null, true);
+ if ($tokens[$objOperator]['code'] === T_DOUBLE_COLON) {
+ // The variable lives within a class, and is referenced like
+ // this: MyClass::$_variable, so we don't know its scope.
+ $inClass = true;
+ } else {
+ $inClass = $phpcsFile->hasCondition($stackPtr, array(T_CLASS, T_INTERFACE, T_TRAIT));
+ }
+
+ if ($inClass === true) {
+ $varName = substr($varName, 1);
+ }
+ }
+
+ if (Common::isCamelCaps($varName, false, true, false) === false) {
+ $error = 'Variable "%s" is not in valid camel caps format';
+ $data = array($originalVarName);
+ $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $data);
+ } else if (preg_match('|\d|', $varName) === 1) {
+ $warning = 'Variable "%s" contains numbers but this is discouraged';
+ $data = array($originalVarName);
+ $phpcsFile->addWarning($warning, $stackPtr, 'ContainsNumbers', $data);
+ }
+
+ }//end processVariable()
+
+
+ /**
+ * Processes class member variables.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token in the
+ * stack passed in $tokens.
+ *
+ * @return void
+ */
+ protected function processMemberVar(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $varName = ltrim($tokens[$stackPtr]['content'], '$');
+ $memberProps = $phpcsFile->getMemberProperties($stackPtr);
+ $public = ($memberProps['scope'] === 'public');
+
+ if ($public === true) {
+ if (substr($varName, 0, 1) === '_') {
+ $error = 'Public member variable "%s" must not contain a leading underscore';
+ $data = array($varName);
+ $phpcsFile->addError($error, $stackPtr, 'PublicHasUnderscore', $data);
+ return;
+ }
+ } else {
+ if (substr($varName, 0, 1) !== '_') {
+ $scope = ucfirst($memberProps['scope']);
+ $error = '%s member variable "%s" must contain a leading underscore';
+ $data = array(
+ $scope,
+ $varName,
+ );
+ $phpcsFile->addError($error, $stackPtr, 'PrivateNoUnderscore', $data);
+ return;
+ }
+ }
+
+ if (Common::isCamelCaps($varName, false, $public, false) === false) {
+ $error = 'Member variable "%s" is not in valid camel caps format';
+ $data = array($varName);
+ $phpcsFile->addError($error, $stackPtr, 'MemberVarNotCamelCaps', $data);
+ } else if (preg_match('|\d|', $varName) === 1) {
+ $warning = 'Member variable "%s" contains numbers but this is discouraged';
+ $data = array($varName);
+ $phpcsFile->addWarning($warning, $stackPtr, 'MemberVarContainsNumbers', $data);
+ }
+
+ }//end processMemberVar()
+
+
+ /**
+ * Processes the variable found within a double quoted string.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the double quoted
+ * string.
+ *
+ * @return void
+ */
+ protected function processVariableInString(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $phpReservedVars = array(
+ '_SERVER',
+ '_GET',
+ '_POST',
+ '_REQUEST',
+ '_SESSION',
+ '_ENV',
+ '_COOKIE',
+ '_FILES',
+ 'GLOBALS',
+ 'http_response_header',
+ 'HTTP_RAW_POST_DATA',
+ 'php_errormsg',
+ );
+
+ if (preg_match_all('|[^\\\]\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)|', $tokens[$stackPtr]['content'], $matches) !== 0) {
+ foreach ($matches[1] as $varName) {
+ // If it's a php reserved var, then its ok.
+ if (in_array($varName, $phpReservedVars) === true) {
+ continue;
+ }
+
+ if (Common::isCamelCaps($varName, false, true, false) === false) {
+ $error = 'Variable "%s" is not in valid camel caps format';
+ $data = array($varName);
+ $phpcsFile->addError($error, $stackPtr, 'StringVarNotCamelCaps', $data);
+ } else if (preg_match('|\d|', $varName) === 1) {
+ $warning = 'Variable "%s" contains numbers but this is discouraged';
+ $data = array($varName);
+ $phpcsFile->addWarning($warning, $stackPtr, 'StringVarContainsNumbers', $data);
+ }
+ }//end foreach
+ }//end if
+
+ }//end processVariableInString()
+
+
+}//end class
--- /dev/null
+<?php
+function myFunction($variable) {
+ // $variable is unused.
+ echo 'hi';
+}
+?>
--- /dev/null
+<?php
+/**
+ * Unit test class for the CodeAnalyzer 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\Standards\Zend\Tests\Debug;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Config;
+
+class CodeAnalyzerUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Should this test be skipped for some reason.
+ *
+ * @return void
+ */
+ protected function shouldSkipTest()
+ {
+ $analyzerPath = Config::getExecutablePath('zend_ca');
+ return (is_null($analyzerPath));
+
+ }//end shouldSkipTest()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array();
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(2 => 1);
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+
+echo 'hi';
+
+?>
+
+<?php
+
+echo 'bye';
+
+?>
+
--- /dev/null
+<div class="clear"></div>
+ <?php include('inc.php'); ?>
+</div>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Unit test class for the ClosingTag 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\Standards\Zend\Tests\Files;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ClosingTagUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @param string $testFile The name of the file being tested.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList($testFile='')
+ {
+ if ($testFile !== 'ClosingTagUnitTest.1.inc') {
+ return array();
+ }
+
+ return array(11 => 1);
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array();
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?php
+$varName = 'hello';
+$var_name = 'hello';
+$varname = 'hello';
+$_varName = 'hello';
+$varname2 = 'hello';
+
+class MyClass
+{
+ $varName = 'hello';
+ $var_name = 'hello';
+ $varname = 'hello';
+ $_varName = 'hello';
+ $varName2 = 'hello';
+
+ public $varName = 'hello';
+ public $var_name = 'hello';
+ public $varname = 'hello';
+ public $_varName = 'hello';
+ public $varName2 = 'hello';
+
+ protected $_varName = 'hello';
+ protected $_var_name = 'hello';
+ protected $_varname = 'hello';
+ protected $varName = 'hello';
+ protected $_varName2 = 'hello';
+
+ private $_varName = 'hello';
+ private $_var_name = 'hello';
+ private $_varname = 'hello';
+ private $varName = 'hello';
+ private $_varName2 = 'hello';
+}
+
+echo $varName;
+echo $var_name;
+echo $varname;
+echo $_varName;
+echo $varName2;
+
+echo "Hello $varName";
+echo "Hello $var_name";
+echo "Hello $varname";
+echo "Hello $_varName";
+echo "Hello $varName2";
+
+echo 'Hello '.$varName;
+echo 'Hello '.$var_name;
+echo 'Hello '.$varname;
+echo 'Hello '.$_varName;
+echo 'Hello '.$varName2;
+
+echo $_SERVER['var_name'];
+echo $_REQUEST['var_name'];
+echo $_GET['var_name'];
+echo $_POST['var_name'];
+echo $GLOBALS['var_name'];
+echo $GLOBALS['var_name2'];
+
+echo MyClass::$varName;
+echo MyClass::$var_name;
+echo MyClass::$varname;
+echo MyClass::$_varName;
+echo MyClass::$varName2;
+
+echo $this->varName;
+echo $this->var_name;
+echo $this->varname;
+echo $this->_varName;
+echo $this->varName2;
+echo $object->varName;
+echo $object->var_name;
+echo $object->varName2;
+echo $object_name->varname;
+echo $object_name->_varName;
+echo $object_name->varName2;
+
+echo $this->myFunction($one, $two);
+echo $object->myFunction($one_two, $var2);
+
+$error = "format is \$GLOBALS['$varName']";
+$error = "format is \$GLOBALS['$varName2']";
+
+echo $_SESSION['var_name'];
+echo $_FILES['var_name'];
+echo $_ENV['var_name'];
+echo $_COOKIE['var_name'];
+echo $_COOKIE['var_name2'];
+
+$XML = 'hello';
+$myXML = 'hello';
+$XMLParser = 'hello';
+$xmlParser = 'hello';
+$xmlParser2 = 'hello';
+
+echo "{$_SERVER['HOSTNAME']} $var_name";
+
+$someObject->{$name};
+$someObject->my_function($var_name);
+
+var_dump($http_response_header);
+var_dump($HTTP_RAW_POST_DATA);
+var_dump($php_errormsg);
--- /dev/null
+<?php
+/**
+ * Unit test class for the ValidVariableName 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\Standards\Zend\Tests\NamingConventions;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class ValidVariableNameUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getErrorList()
+ {
+ return array(
+ 3 => 1,
+ 5 => 1,
+ 11 => 1,
+ 13 => 1,
+ 17 => 1,
+ 19 => 1,
+ 23 => 1,
+ 25 => 1,
+ 29 => 1,
+ 31 => 1,
+ 36 => 1,
+ 38 => 1,
+ 42 => 1,
+ 44 => 1,
+ 48 => 1,
+ 50 => 1,
+ 61 => 1,
+ 67 => 1,
+ 72 => 1,
+ 74 => 1,
+ 75 => 1,
+ 76 => 1,
+ 79 => 1,
+ 96 => 1,
+ 99 => 1,
+ );
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ public function getWarningList()
+ {
+ return array(
+ 6 => 1,
+ 14 => 1,
+ 20 => 1,
+ 26 => 1,
+ 32 => 1,
+ 39 => 1,
+ 45 => 1,
+ 51 => 1,
+ 64 => 1,
+ 70 => 1,
+ 73 => 1,
+ 76 => 1,
+ 79 => 1,
+ 82 => 1,
+ 94 => 1,
+ );
+
+ }//end getWarningList()
+
+
+}//end class
--- /dev/null
+<?xml version="1.0"?>
+<ruleset name="Zend">
+ <description>A coding standard based on an early Zend Framework coding standard. Note that this standard is out of date.</description>
+
+ <!-- Include some sniffs from all around the place -->
+ <rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
+ <rule ref="Generic.Functions.OpeningFunctionBraceBsdAllman"/>
+ <rule ref="Generic.PHP.DisallowShortOpenTag"/>
+ <rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
+ <rule ref="PEAR.Classes.ClassDeclaration"/>
+ <rule ref="PEAR.ControlStructures.ControlSignature"/>
+ <rule ref="PEAR.Functions.FunctionCallSignature"/>
+ <rule ref="PEAR.Functions.ValidDefaultValue"/>
+ <rule ref="PEAR.WhiteSpace.ScopeClosingBrace"/>
+ <rule ref="Squiz.Functions.GlobalFunction"/>
+
+ <!-- Lines can be 80 chars long, show errors at 120 chars -->
+ <rule ref="Generic.Files.LineLength">
+ <properties>
+ <property name="lineLimit" value="80"/>
+ <property name="absoluteLineLimit" value="120"/>
+ </properties>
+ </rule>
+
+ <!-- Use Unix newlines -->
+ <rule ref="Generic.Files.LineEndings">
+ <properties>
+ <property name="eolChar" value="\n"/>
+ </properties>
+ </rule>
+
+</ruleset>
--- /dev/null
+<?php
+/**
+ * Tokenizes CSS 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\Tokenizers;
+
+use PHP_CodeSniffer\Util;
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Exceptions\TokenizerException;
+
+class CSS extends PHP
+{
+
+
+ /**
+ * Initialise the tokenizer.
+ *
+ * Pre-checks the content to see if it looks minified.
+ *
+ * @param string $content The content to tokenize,
+ * @param \PHP_CodeSniffer\Config $config The config data for the run.
+ * @param string $eolChar The EOL char used in the content.
+ *
+ * @return void
+ * @throws TokenizerException If the file appears to be minified.
+ */
+ public function __construct($content, Config $config, $eolChar='\n')
+ {
+ if ($this->isMinifiedContent($content, $eolChar) === true) {
+ throw new TokenizerException('File appears to be minified and cannot be processed');
+ }
+
+ return parent::__construct($content, $config, $eolChar);
+
+ }//end __construct()
+
+
+ /**
+ * Creates an array of tokens when given some CSS code.
+ *
+ * Uses the PHP tokenizer to do all the tricky work
+ *
+ * @param string $string The string to tokenize.
+ *
+ * @return array
+ */
+ public function tokenize($string)
+ {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** START CSS TOKENIZING 1ST PASS ***".PHP_EOL;
+ }
+
+ // If the content doesn't have an EOL char on the end, add one so
+ // the open and close tags we add are parsed correctly.
+ $eolAdded = false;
+ if (substr($string, (strlen($this->eolChar) * -1)) !== $this->eolChar) {
+ $string .= $this->eolChar;
+ $eolAdded = true;
+ }
+
+ $string = str_replace('<?php', '^PHPCS_CSS_T_OPEN_TAG^', $string);
+ $string = str_replace('?>', '^PHPCS_CSS_T_CLOSE_TAG^', $string);
+ $tokens = parent::tokenize('<?php '.$string.'?>');
+
+ $finalTokens = array();
+ $finalTokens[0] = array(
+ 'code' => T_OPEN_TAG,
+ 'type' => 'T_OPEN_TAG',
+ 'content' => '',
+ );
+
+ $newStackPtr = 1;
+ $numTokens = count($tokens);
+ $multiLineComment = false;
+ for ($stackPtr = 1; $stackPtr < $numTokens; $stackPtr++) {
+ $token = $tokens[$stackPtr];
+
+ // CSS files don't have lists, breaks etc, so convert these to
+ // standard strings early so they can be converted into T_STYLE
+ // tokens and joined with other strings if needed.
+ if ($token['code'] === T_BREAK
+ || $token['code'] === T_LIST
+ || $token['code'] === T_DEFAULT
+ || $token['code'] === T_SWITCH
+ || $token['code'] === T_FOR
+ || $token['code'] === T_FOREACH
+ || $token['code'] === T_WHILE
+ || $token['code'] === T_DEC
+ ) {
+ $token['type'] = 'T_STRING';
+ $token['code'] = T_STRING;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $token['type'];
+ $content = Util\Common::prepareForOutput($token['content']);
+ echo "\tProcess token $stackPtr: $type => $content".PHP_EOL;
+ }
+
+ if ($token['code'] === T_BITWISE_XOR
+ && $tokens[($stackPtr + 1)]['content'] === 'PHPCS_CSS_T_OPEN_TAG'
+ ) {
+ $content = '<?php';
+ for ($stackPtr = ($stackPtr + 3); $stackPtr < $numTokens; $stackPtr++) {
+ if ($tokens[$stackPtr]['code'] === T_BITWISE_XOR
+ && $tokens[($stackPtr + 1)]['content'] === 'PHPCS_CSS_T_CLOSE_TAG'
+ ) {
+ // Add the end tag and ignore the * we put at the end.
+ $content .= '?>';
+ $stackPtr += 2;
+ break;
+ } else {
+ $content .= $tokens[$stackPtr]['content'];
+ }
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t=> Found embedded PHP code: ";
+ $cleanContent = Util\Common::prepareForOutput($content);
+ echo $cleanContent.PHP_EOL;
+ }
+
+ $finalTokens[$newStackPtr] = array(
+ 'type' => 'T_EMBEDDED_PHP',
+ 'code' => T_EMBEDDED_PHP,
+ 'content' => $content,
+ );
+
+ $newStackPtr++;
+ continue;
+ }//end if
+
+ if ($token['code'] === T_GOTO_LABEL) {
+ // Convert these back to T_STRING followed by T_COLON so we can
+ // more easily process style definitions.
+ $finalTokens[$newStackPtr] = array(
+ 'type' => 'T_STRING',
+ 'code' => T_STRING,
+ 'content' => substr($token['content'], 0, -1),
+ );
+ $newStackPtr++;
+ $finalTokens[$newStackPtr] = array(
+ 'type' => 'T_COLON',
+ 'code' => T_COLON,
+ 'content' => ':',
+ );
+ $newStackPtr++;
+ continue;
+ }
+
+ if ($token['code'] === T_FUNCTION) {
+ // There are no functions in CSS, so convert this to a string.
+ $finalTokens[$newStackPtr] = array(
+ 'type' => 'T_STRING',
+ 'code' => T_STRING,
+ 'content' => $token['content'],
+ );
+
+ $newStackPtr++;
+ continue;
+ }
+
+ if ($token['code'] === T_COMMENT
+ && substr($token['content'], 0, 2) === '/*'
+ ) {
+ // Multi-line comment. Record it so we can ignore other
+ // comment tags until we get out of this one.
+ $multiLineComment = true;
+ }
+
+ if ($token['code'] === T_COMMENT
+ && $multiLineComment === false
+ && (substr($token['content'], 0, 2) === '//'
+ || $token['content']{0} === '#')
+ ) {
+ $content = ltrim($token['content'], '#/');
+
+ // Guard against PHP7+ syntax errors by stripping
+ // leading zeros so the content doesn't look like an invalid int.
+ $leadingZero = false;
+ if ($content{0} === '0') {
+ $content = '1'.$content;
+ $leadingZero = true;
+ }
+
+ $commentTokens = parent::tokenize('<?php '.$content.'?>');
+
+ // The first and last tokens are the open/close tags.
+ array_shift($commentTokens);
+ array_pop($commentTokens);
+
+ if ($leadingZero === true) {
+ $commentTokens[0]['content'] = substr($commentTokens[0]['content'], 1);
+ $content = substr($content, 1);
+ }
+
+ if ($token['content']{0} === '#') {
+ // The # character is not a comment in CSS files, so
+ // determine what it means in this context.
+ $firstContent = $commentTokens[0]['content'];
+
+ // If the first content is just a number, it is probably a
+ // colour like 8FB7DB, which PHP splits into 8 and FB7DB.
+ if (($commentTokens[0]['code'] === T_LNUMBER
+ || $commentTokens[0]['code'] === T_DNUMBER)
+ && $commentTokens[1]['code'] === T_STRING
+ ) {
+ $firstContent .= $commentTokens[1]['content'];
+ array_shift($commentTokens);
+ }
+
+ // If the first content looks like a colour and not a class
+ // definition, join the tokens together.
+ if (preg_match('/^[ABCDEF0-9]+$/i', $firstContent) === 1
+ && $commentTokens[1]['content'] !== '-'
+ ) {
+ array_shift($commentTokens);
+ // Work out what we trimmed off above and remember to re-add it.
+ $trimmed = substr($token['content'], 0, (strlen($token['content']) - strlen($content)));
+ $finalTokens[$newStackPtr] = array(
+ 'type' => 'T_COLOUR',
+ 'code' => T_COLOUR,
+ 'content' => $trimmed.$firstContent,
+ );
+ } else {
+ $finalTokens[$newStackPtr] = array(
+ 'type' => 'T_HASH',
+ 'code' => T_HASH,
+ 'content' => '#',
+ );
+ }
+ } else {
+ $finalTokens[$newStackPtr] = array(
+ 'type' => 'T_STRING',
+ 'code' => T_STRING,
+ 'content' => '//',
+ );
+ }//end if
+
+ $newStackPtr++;
+
+ array_splice($tokens, $stackPtr, 1, $commentTokens);
+ $numTokens = count($tokens);
+ $stackPtr--;
+ continue;
+ }//end if
+
+ if ($token['code'] === T_COMMENT
+ && substr($token['content'], -2) === '*/'
+ ) {
+ // Multi-line comment is done.
+ $multiLineComment = false;
+ }
+
+ $finalTokens[$newStackPtr] = $token;
+ $newStackPtr++;
+ }//end for
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** END CSS TOKENIZING 1ST PASS ***".PHP_EOL;
+ echo "\t*** START CSS TOKENIZING 2ND PASS ***".PHP_EOL;
+ }
+
+ // A flag to indicate if we are inside a style definition,
+ // which is defined using curly braces.
+ $inStyleDef = false;
+
+ // A flag to indicate if an At-rule like "@media" is used, which will result
+ // in nested curly brackets.
+ $asperandStart = false;
+
+ $numTokens = count($finalTokens);
+ for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) {
+ $token = $finalTokens[$stackPtr];
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $token['type'];
+ $content = Util\Common::prepareForOutput($token['content']);
+ echo "\tProcess token $stackPtr: $type => $content".PHP_EOL;
+ }
+
+ switch ($token['code']) {
+ case T_OPEN_CURLY_BRACKET:
+ // Opening curly brackets for an At-rule do not start a style
+ // definition. We also reset the asperand flag here because the next
+ // opening curly bracket could be indeed the start of a style
+ // definition.
+ if ($asperandStart === true) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ if ($inStyleDef === true) {
+ echo "\t\t* style definition closed *".PHP_EOL;
+ }
+
+ if ($asperandStart === true) {
+ echo "\t\t* at-rule definition closed *".PHP_EOL;
+ }
+ }
+
+ $inStyleDef = false;
+ $asperandStart = false;
+ } else {
+ $inStyleDef = true;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* style definition opened *".PHP_EOL;
+ }
+ }
+ break;
+ case T_CLOSE_CURLY_BRACKET:
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ if ($inStyleDef === true) {
+ echo "\t\t* style definition closed *".PHP_EOL;
+ }
+
+ if ($asperandStart === true) {
+ echo "\t\t* at-rule definition closed *".PHP_EOL;
+ }
+ }
+
+ $inStyleDef = false;
+ $asperandStart = false;
+ break;
+ case T_MINUS:
+ // Minus signs are often used instead of spaces inside
+ // class names, IDs and styles.
+ if ($finalTokens[($stackPtr + 1)]['code'] === T_STRING) {
+ if ($finalTokens[($stackPtr - 1)]['code'] === T_STRING) {
+ $newContent = $finalTokens[($stackPtr - 1)]['content'].'-'.$finalTokens[($stackPtr + 1)]['content'];
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* token is a string joiner; ignoring this and previous token".PHP_EOL;
+ $old = Util\Common::prepareForOutput($finalTokens[($stackPtr + 1)]['content']);
+ $new = Util\Common::prepareForOutput($newContent);
+ echo "\t\t=> token ".($stackPtr + 1)." content changed from \"$old\" to \"$new\"".PHP_EOL;
+ }
+
+ $finalTokens[($stackPtr + 1)]['content'] = $newContent;
+ unset($finalTokens[$stackPtr]);
+ unset($finalTokens[($stackPtr - 1)]);
+ } else {
+ $newContent = '-'.$finalTokens[($stackPtr + 1)]['content'];
+
+ $finalTokens[($stackPtr + 1)]['content'] = $newContent;
+ unset($finalTokens[$stackPtr]);
+ }
+ } else if ($finalTokens[($stackPtr + 1)]['code'] === T_LNUMBER) {
+ // They can also be used to provide negative numbers.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* token is part of a negative number; adding content to next token and ignoring *".PHP_EOL;
+ $content = Util\Common::prepareForOutput($finalTokens[($stackPtr + 1)]['content']);
+ echo "\t\t=> token ".($stackPtr + 1)." content changed from \"$content\" to \"-$content\"".PHP_EOL;
+ }
+
+ $finalTokens[($stackPtr + 1)]['content'] = '-'.$finalTokens[($stackPtr + 1)]['content'];
+ unset($finalTokens[$stackPtr]);
+ }//end if
+
+ break;
+ case T_COLON:
+ // Only interested in colons that are defining styles.
+ if ($inStyleDef === false) {
+ break;
+ }
+
+ for ($x = ($stackPtr - 1); $x >= 0; $x--) {
+ if (isset(Util\Tokens::$emptyTokens[$finalTokens[$x]['code']]) === false) {
+ break;
+ }
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $finalTokens[$x]['type'];
+ echo "\t\t=> token $x changed from $type to T_STYLE".PHP_EOL;
+ }
+
+ $finalTokens[$x]['type'] = 'T_STYLE';
+ $finalTokens[$x]['code'] = T_STYLE;
+ break;
+ case T_STRING:
+ if (strtolower($token['content']) === 'url') {
+ // Find the next content.
+ for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
+ if (isset(Util\Tokens::$emptyTokens[$finalTokens[$x]['code']]) === false) {
+ break;
+ }
+ }
+
+ // Needs to be in the format "url(" for it to be a URL.
+ if ($finalTokens[$x]['code'] !== T_OPEN_PARENTHESIS) {
+ continue;
+ }
+
+ // Make sure the content isn't empty.
+ for ($y = ($x + 1); $y < $numTokens; $y++) {
+ if (isset(Util\Tokens::$emptyTokens[$finalTokens[$y]['code']]) === false) {
+ break;
+ }
+ }
+
+ if ($finalTokens[$y]['code'] === T_CLOSE_PARENTHESIS) {
+ continue;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ for ($i = ($stackPtr + 1); $i <= $y; $i++) {
+ $type = $finalTokens[$i]['type'];
+ $content = Util\Common::prepareForOutput($finalTokens[$i]['content']);
+ echo "\tProcess token $i: $type => $content".PHP_EOL;
+ }
+
+ echo "\t\t* token starts a URL *".PHP_EOL;
+ }
+
+ // Join all the content together inside the url() statement.
+ $newContent = '';
+ for ($i = ($x + 2); $i < $numTokens; $i++) {
+ if ($finalTokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
+ break;
+ }
+
+ $newContent .= $finalTokens[$i]['content'];
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($finalTokens[$i]['content']);
+ echo "\t\t=> token $i added to URL string and ignored: $content".PHP_EOL;
+ }
+
+ unset($finalTokens[$i]);
+ }
+
+ $stackPtr = $i;
+
+ // If the content inside the "url()" is in double quotes
+ // there will only be one token and so we don't have to do
+ // anything except change its type. If it is not empty,
+ // we need to do some token merging.
+ $finalTokens[($x + 1)]['type'] = 'T_URL';
+ $finalTokens[($x + 1)]['code'] = T_URL;
+
+ if ($newContent !== '') {
+ $finalTokens[($x + 1)]['content'] .= $newContent;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($finalTokens[($x + 1)]['content']);
+ echo "\t\t=> token content changed to: $content".PHP_EOL;
+ }
+ }
+ } else if ($finalTokens[$stackPtr]['content'][0] === '-'
+ && $finalTokens[($stackPtr + 1)]['code'] === T_STRING
+ ) {
+ if (isset($finalTokens[($stackPtr - 1)]) === true
+ && $finalTokens[($stackPtr - 1)]['code'] === T_STRING
+ ) {
+ $newContent = $finalTokens[($stackPtr - 1)]['content'].$finalTokens[$stackPtr]['content'].$finalTokens[($stackPtr + 1)]['content'];
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* token is a string joiner; ignoring this and previous token".PHP_EOL;
+ $old = Util\Common::prepareForOutput($finalTokens[($stackPtr + 1)]['content']);
+ $new = Util\Common::prepareForOutput($newContent);
+ echo "\t\t=> token ".($stackPtr + 1)." content changed from \"$old\" to \"$new\"".PHP_EOL;
+ }
+
+ $finalTokens[($stackPtr + 1)]['content'] = $newContent;
+ unset($finalTokens[$stackPtr]);
+ unset($finalTokens[($stackPtr - 1)]);
+ } else {
+ $newContent = $finalTokens[$stackPtr]['content'].$finalTokens[($stackPtr + 1)]['content'];
+
+ $finalTokens[($stackPtr + 1)]['content'] = $newContent;
+ unset($finalTokens[$stackPtr]);
+ }
+ }//end if
+
+ break;
+ case T_ASPERAND:
+ $asperandStart = true;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* at-rule definition opened *".PHP_EOL;
+ }
+ break;
+ default:
+ // Nothing special to be done with this token.
+ break;
+ }//end switch
+ }//end for
+
+ // Reset the array keys to avoid gaps.
+ $finalTokens = array_values($finalTokens);
+ $numTokens = count($finalTokens);
+
+ // Blank out the content of the end tag.
+ $finalTokens[($numTokens - 1)]['content'] = '';
+
+ if ($eolAdded === true) {
+ // Strip off the extra EOL char we added for tokenizing.
+ $finalTokens[($numTokens - 2)]['content'] = substr(
+ $finalTokens[($numTokens - 2)]['content'],
+ 0,
+ (strlen($this->eolChar) * -1)
+ );
+
+ if ($finalTokens[($numTokens - 2)]['content'] === '') {
+ unset($finalTokens[($numTokens - 2)]);
+ $finalTokens = array_values($finalTokens);
+ $numTokens = count($finalTokens);
+ }
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** END CSS TOKENIZING 2ND PASS ***".PHP_EOL;
+ }
+
+ return $finalTokens;
+
+ }//end tokenize()
+
+
+ /**
+ * Performs additional processing after main tokenizing.
+ *
+ * @return void
+ */
+ public function processAdditional()
+ {
+ /*
+ We override this method because we don't want the PHP version to
+ run during CSS processing because it is wasted processing time.
+ */
+
+ }//end processAdditional()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tokenizes doc block comments.
+ *
+ * @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\Tokenizers;
+
+use PHP_CodeSniffer\Util;
+
+class Comment
+{
+
+
+ /**
+ * Creates an array of tokens when given some PHP code.
+ *
+ * Starts by using token_get_all() but does a lot of extra processing
+ * to insert information about the context of the token.
+ *
+ * @param string $string The string to tokenize.
+ * @param string $eolChar The EOL character to use for splitting strings.
+ * @param int $stackPtr The position of the first token in the file.
+ *
+ * @return array
+ */
+ public function tokenizeString($string, $eolChar, $stackPtr)
+ {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t*** START COMMENT TOKENIZING ***".PHP_EOL;
+ }
+
+ $tokens = array();
+ $numChars = strlen($string);
+
+ /*
+ Doc block comments start with /*, but typically contain an
+ extra star when they are used for function and class comments.
+ */
+
+ $char = ($numChars - strlen(ltrim($string, '/*')));
+ $openTag = substr($string, 0, $char);
+ $string = ltrim($string, '/*');
+
+ $tokens[$stackPtr] = array(
+ 'content' => $openTag,
+ 'code' => T_DOC_COMMENT_OPEN_TAG,
+ 'type' => 'T_DOC_COMMENT_OPEN_TAG',
+ 'comment_tags' => array(),
+ );
+
+ $openPtr = $stackPtr;
+ $stackPtr++;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($openTag);
+ echo "\t\tCreate comment token: T_DOC_COMMENT_OPEN_TAG => $content".PHP_EOL;
+ }
+
+ /*
+ Strip off the close tag so it doesn't interfere with any
+ of our comment line processing. The token will be added to the
+ stack just before we return it.
+ */
+
+ $closeTag = array(
+ 'content' => substr($string, strlen(rtrim($string, '/*'))),
+ 'code' => T_DOC_COMMENT_CLOSE_TAG,
+ 'type' => 'T_DOC_COMMENT_CLOSE_TAG',
+ 'comment_opener' => $openPtr,
+ );
+
+ if ($closeTag['content'] === false) {
+ $closeTag['content'] = '';
+ }
+
+ $string = rtrim($string, '/*');
+
+ /*
+ Process each line of the comment.
+ */
+
+ $lines = explode($eolChar, $string);
+ $numLines = count($lines);
+ foreach ($lines as $lineNum => $string) {
+ if ($lineNum !== ($numLines - 1)) {
+ $string .= $eolChar;
+ }
+
+ $char = 0;
+ $numChars = strlen($string);
+
+ // We've started a new line, so process the indent.
+ $space = $this->collectWhitespace($string, $char, $numChars);
+ if ($space !== null) {
+ $tokens[$stackPtr] = $space;
+ $stackPtr++;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($space['content']);
+ echo "\t\tCreate comment token: T_DOC_COMMENT_WHITESPACE => $content".PHP_EOL;
+ }
+
+ $char += strlen($space['content']);
+ if ($char === $numChars) {
+ break;
+ }
+ }
+
+ if ($string === '') {
+ continue;
+ }
+
+ if ($string[$char] === '*') {
+ // This is a function or class doc block line.
+ $char++;
+ $tokens[$stackPtr] = array(
+ 'content' => '*',
+ 'code' => T_DOC_COMMENT_STAR,
+ 'type' => 'T_DOC_COMMENT_STAR',
+ );
+
+ $stackPtr++;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\tCreate comment token: T_DOC_COMMENT_STAR => *".PHP_EOL;
+ }
+ }
+
+ // Now we are ready to process the actual content of the line.
+ $lineTokens = $this->processLine($string, $eolChar, $char, $numChars);
+ foreach ($lineTokens as $lineToken) {
+ $tokens[$stackPtr] = $lineToken;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($lineToken['content']);
+ $type = $lineToken['type'];
+ echo "\t\tCreate comment token: $type => $content".PHP_EOL;
+ }
+
+ if ($lineToken['code'] === T_DOC_COMMENT_TAG) {
+ $tokens[$openPtr]['comment_tags'][] = $stackPtr;
+ }
+
+ $stackPtr++;
+ }
+ }//end foreach
+
+ $tokens[$stackPtr] = $closeTag;
+ $tokens[$openPtr]['comment_closer'] = $stackPtr;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($closeTag['content']);
+ echo "\t\tCreate comment token: T_DOC_COMMENT_CLOSE_TAG => $content".PHP_EOL;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t*** END COMMENT TOKENIZING ***".PHP_EOL;
+ }
+
+ return $tokens;
+
+ }//end tokenizeString()
+
+
+ /**
+ * Process a single line of a comment.
+ *
+ * @param string $string The comment string being tokenized.
+ * @param string $eolChar The EOL character to use for splitting strings.
+ * @param int $start The position in the string to start processing.
+ * @param int $end The position in the string to end processing.
+ *
+ * @return array
+ */
+ private function processLine($string, $eolChar, $start, $end)
+ {
+ $tokens = array();
+
+ // Collect content padding.
+ $space = $this->collectWhitespace($string, $start, $end);
+ if ($space !== null) {
+ $tokens[] = $space;
+ $start += strlen($space['content']);
+ }
+
+ if (isset($string[$start]) === false) {
+ return $tokens;
+ }
+
+ if ($string[$start] === '@') {
+ // The content up until the first whitespace is the tag name.
+ $matches = array();
+ preg_match('/@[^\s]+/', $string, $matches, 0, $start);
+ if (isset($matches[0]) === true) {
+ $tagName = $matches[0];
+ $start += strlen($tagName);
+ $tokens[] = array(
+ 'content' => $tagName,
+ 'code' => T_DOC_COMMENT_TAG,
+ 'type' => 'T_DOC_COMMENT_TAG',
+ );
+
+ // Then there will be some whitespace.
+ $space = $this->collectWhitespace($string, $start, $end);
+ if ($space !== null) {
+ $tokens[] = $space;
+ $start += strlen($space['content']);
+ }
+ }
+ }//end if
+
+ // Process the rest of the line.
+ $eol = strpos($string, $eolChar, $start);
+ if ($eol === false) {
+ $eol = $end;
+ }
+
+ if ($eol > $start) {
+ $tokens[] = array(
+ 'content' => substr($string, $start, ($eol - $start)),
+ 'code' => T_DOC_COMMENT_STRING,
+ 'type' => 'T_DOC_COMMENT_STRING',
+ );
+ }
+
+ if ($eol !== $end) {
+ $tokens[] = array(
+ 'content' => substr($string, $eol, strlen($eolChar)),
+ 'code' => T_DOC_COMMENT_WHITESPACE,
+ 'type' => 'T_DOC_COMMENT_WHITESPACE',
+ );
+ }
+
+ return $tokens;
+
+ }//end processLine()
+
+
+ /**
+ * Collect consecutive whitespace into a single token.
+ *
+ * @param string $string The comment string being tokenized.
+ * @param int $start The position in the string to start processing.
+ * @param int $end The position in the string to end processing.
+ *
+ * @return array|null
+ */
+ private function collectWhitespace($string, $start, $end)
+ {
+ $space = '';
+ for ($start; $start < $end; $start++) {
+ if ($string[$start] !== ' ' && $string[$start] !== "\t") {
+ break;
+ }
+
+ $space .= $string[$start];
+ }
+
+ if ($space === '') {
+ return null;
+ }
+
+ $token = array(
+ 'content' => $space,
+ 'code' => T_DOC_COMMENT_WHITESPACE,
+ 'type' => 'T_DOC_COMMENT_WHITESPACE',
+ );
+
+ return $token;
+
+ }//end collectWhitespace()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tokenizes JS 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\Tokenizers;
+
+use PHP_CodeSniffer\Util;
+use PHP_CodeSniffer\Exceptions\TokenizerException;
+use PHP_CodeSniffer\Config;
+
+class JS extends Tokenizer
+{
+
+
+ /**
+ * A list of tokens that are allowed to open a scope.
+ *
+ * This array also contains information about what kind of token the scope
+ * opener uses to open and close the scope, if the token strictly requires
+ * an opener, if the token can share a scope closer, and who it can be shared
+ * with. An example of a token that shares a scope closer is a CASE scope.
+ *
+ * @var array
+ */
+ public $scopeOpeners = array(
+ T_IF => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_TRY => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_CATCH => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_ELSE => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_FOR => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_CLASS => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_FUNCTION => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_WHILE => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_DO => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_SWITCH => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_CASE => array(
+ 'start' => array(T_COLON => T_COLON),
+ 'end' => array(
+ T_BREAK => T_BREAK,
+ T_RETURN => T_RETURN,
+ T_CONTINUE => T_CONTINUE,
+ T_THROW => T_THROW,
+ ),
+ 'strict' => true,
+ 'shared' => true,
+ 'with' => array(
+ T_DEFAULT => T_DEFAULT,
+ T_CASE => T_CASE,
+ T_SWITCH => T_SWITCH,
+ ),
+ ),
+ T_DEFAULT => array(
+ 'start' => array(T_COLON => T_COLON),
+ 'end' => array(
+ T_BREAK => T_BREAK,
+ T_RETURN => T_RETURN,
+ T_CONTINUE => T_CONTINUE,
+ T_THROW => T_THROW,
+ ),
+ 'strict' => true,
+ 'shared' => true,
+ 'with' => array(
+ T_CASE => T_CASE,
+ T_SWITCH => T_SWITCH,
+ ),
+ ),
+ );
+
+ /**
+ * A list of tokens that end the scope.
+ *
+ * This array is just a unique collection of the end tokens
+ * from the _scopeOpeners array. The data is duplicated here to
+ * save time during parsing of the file.
+ *
+ * @var array
+ */
+ public $endScopeTokens = array(
+ T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
+ T_BREAK => T_BREAK,
+ );
+
+ /**
+ * A list of special JS tokens and their types.
+ *
+ * @var array
+ */
+ protected $tokenValues = array(
+ 'class' => 'T_CLASS',
+ 'function' => 'T_FUNCTION',
+ 'prototype' => 'T_PROTOTYPE',
+ 'try' => 'T_TRY',
+ 'catch' => 'T_CATCH',
+ 'return' => 'T_RETURN',
+ 'throw' => 'T_THROW',
+ 'break' => 'T_BREAK',
+ 'switch' => 'T_SWITCH',
+ 'continue' => 'T_CONTINUE',
+ 'if' => 'T_IF',
+ 'else' => 'T_ELSE',
+ 'do' => 'T_DO',
+ 'while' => 'T_WHILE',
+ 'for' => 'T_FOR',
+ 'var' => 'T_VAR',
+ 'case' => 'T_CASE',
+ 'default' => 'T_DEFAULT',
+ 'true' => 'T_TRUE',
+ 'false' => 'T_FALSE',
+ 'null' => 'T_NULL',
+ 'this' => 'T_THIS',
+ 'typeof' => 'T_TYPEOF',
+ '(' => 'T_OPEN_PARENTHESIS',
+ ')' => 'T_CLOSE_PARENTHESIS',
+ '{' => 'T_OPEN_CURLY_BRACKET',
+ '}' => 'T_CLOSE_CURLY_BRACKET',
+ '[' => 'T_OPEN_SQUARE_BRACKET',
+ ']' => 'T_CLOSE_SQUARE_BRACKET',
+ '?' => 'T_INLINE_THEN',
+ '.' => 'T_OBJECT_OPERATOR',
+ '+' => 'T_PLUS',
+ '-' => 'T_MINUS',
+ '*' => 'T_MULTIPLY',
+ '%' => 'T_MODULUS',
+ '/' => 'T_DIVIDE',
+ '^' => 'T_LOGICAL_XOR',
+ ',' => 'T_COMMA',
+ ';' => 'T_SEMICOLON',
+ ':' => 'T_COLON',
+ '<' => 'T_LESS_THAN',
+ '>' => 'T_GREATER_THAN',
+ '<<' => 'T_SL',
+ '>>' => 'T_SR',
+ '>>>' => 'T_ZSR',
+ '<<=' => 'T_SL_EQUAL',
+ '>>=' => 'T_SR_EQUAL',
+ '>>>=' => 'T_ZSR_EQUAL',
+ '<=' => 'T_IS_SMALLER_OR_EQUAL',
+ '>=' => 'T_IS_GREATER_OR_EQUAL',
+ '=>' => 'T_DOUBLE_ARROW',
+ '!' => 'T_BOOLEAN_NOT',
+ '||' => 'T_BOOLEAN_OR',
+ '&&' => 'T_BOOLEAN_AND',
+ '|' => 'T_BITWISE_OR',
+ '&' => 'T_BITWISE_AND',
+ '!=' => 'T_IS_NOT_EQUAL',
+ '!==' => 'T_IS_NOT_IDENTICAL',
+ '=' => 'T_EQUAL',
+ '==' => 'T_IS_EQUAL',
+ '===' => 'T_IS_IDENTICAL',
+ '-=' => 'T_MINUS_EQUAL',
+ '+=' => 'T_PLUS_EQUAL',
+ '*=' => 'T_MUL_EQUAL',
+ '/=' => 'T_DIV_EQUAL',
+ '%=' => 'T_MOD_EQUAL',
+ '++' => 'T_INC',
+ '--' => 'T_DEC',
+ '//' => 'T_COMMENT',
+ '/*' => 'T_COMMENT',
+ '/**' => 'T_DOC_COMMENT',
+ '*/' => 'T_COMMENT',
+ );
+
+ /**
+ * A list string delimiters.
+ *
+ * @var array
+ */
+ protected $stringTokens = array(
+ '\'' => '\'',
+ '"' => '"',
+ );
+
+ /**
+ * A list tokens that start and end comments.
+ *
+ * @var array
+ */
+ protected $commentTokens = array(
+ '//' => null,
+ '/*' => '*/',
+ '/**' => '*/',
+ );
+
+
+ /**
+ * Initialise the tokenizer.
+ *
+ * Pre-checks the content to see if it looks minified.
+ *
+ * @param string $content The content to tokenize,
+ * @param \PHP_CodeSniffer\Config $config The config data for the run.
+ * @param string $eolChar The EOL char used in the content.
+ *
+ * @return void
+ * @throws TokenizerException If the file appears to be minified.
+ */
+ public function __construct($content, Config $config, $eolChar='\n')
+ {
+ if ($this->isMinifiedContent($content, $eolChar) === true) {
+ throw new TokenizerException('File appears to be minified and cannot be processed');
+ }
+
+ return parent::__construct($content, $config, $eolChar);
+
+ }//end __construct()
+
+
+ /**
+ * Creates an array of tokens when given some JS code.
+ *
+ * @param string $string The string to tokenize.
+ *
+ * @return array
+ */
+ public function tokenize($string)
+ {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** START JS TOKENIZING ***".PHP_EOL;
+ }
+
+ $maxTokenLength = 0;
+ foreach ($this->tokenValues as $token => $values) {
+ if (strlen($token) > $maxTokenLength) {
+ $maxTokenLength = strlen($token);
+ }
+ }
+
+ $tokens = array();
+ $inString = '';
+ $stringChar = null;
+ $inComment = '';
+ $buffer = '';
+ $preStringBuffer = '';
+ $cleanBuffer = false;
+
+ $commentTokenizer = new Comment();
+
+ $tokens[] = array(
+ 'code' => T_OPEN_TAG,
+ 'type' => 'T_OPEN_TAG',
+ 'content' => '',
+ );
+
+ // Convert newlines to single characters for ease of
+ // processing. We will change them back later.
+ $string = str_replace($this->eolChar, "\n", $string);
+
+ $chars = str_split($string);
+ $numChars = count($chars);
+ for ($i = 0; $i < $numChars; $i++) {
+ $char = $chars[$i];
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($char);
+ $bufferContent = Util\Common::prepareForOutput($buffer);
+
+ if ($inString !== '') {
+ echo "\t";
+ }
+
+ if ($inComment !== '') {
+ echo "\t";
+ }
+
+ echo "\tProcess char $i => $content (buffer: $bufferContent)".PHP_EOL;
+ }//end if
+
+ if ($inString === '' && $inComment === '' && $buffer !== '') {
+ // If the buffer only has whitespace and we are about to
+ // add a character, store the whitespace first.
+ if (trim($char) !== '' && trim($buffer) === '') {
+ $tokens[] = array(
+ 'code' => T_WHITESPACE,
+ 'type' => 'T_WHITESPACE',
+ 'content' => str_replace("\n", $this->eolChar, $buffer),
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($buffer);
+ echo "\t=> Added token T_WHITESPACE ($content)".PHP_EOL;
+ }
+
+ $buffer = '';
+ }
+
+ // If the buffer is not whitespace and we are about to
+ // add a whitespace character, store the content first.
+ if ($inString === ''
+ && $inComment === ''
+ && trim($char) === ''
+ && trim($buffer) !== ''
+ ) {
+ $tokens[] = array(
+ 'code' => T_STRING,
+ 'type' => 'T_STRING',
+ 'content' => str_replace("\n", $this->eolChar, $buffer),
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($buffer);
+ echo "\t=> Added token T_STRING ($content)".PHP_EOL;
+ }
+
+ $buffer = '';
+ }
+ }//end if
+
+ // Process strings.
+ if ($inComment === '' && isset($this->stringTokens[$char]) === true) {
+ if ($inString === $char) {
+ // This could be the end of the string, but make sure it
+ // is not escaped first.
+ $escapes = 0;
+ for ($x = ($i - 1); $x >= 0; $x--) {
+ if ($chars[$x] !== '\\') {
+ break;
+ }
+
+ $escapes++;
+ }
+
+ if ($escapes === 0 || ($escapes % 2) === 0) {
+ // There is an even number escape chars,
+ // so this is not escaped, it is the end of the string.
+ $tokens[] = array(
+ 'code' => T_CONSTANT_ENCAPSED_STRING,
+ 'type' => 'T_CONSTANT_ENCAPSED_STRING',
+ 'content' => str_replace("\n", $this->eolChar, $buffer).$char,
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* found end of string *".PHP_EOL;
+ $content = Util\Common::prepareForOutput($buffer.$char);
+ echo "\t=> Added token T_CONSTANT_ENCAPSED_STRING ($content)".PHP_EOL;
+ }
+
+ $buffer = '';
+ $preStringBuffer = '';
+ $inString = '';
+ $stringChar = null;
+ continue;
+ }//end if
+ } else if ($inString === '') {
+ $inString = $char;
+ $stringChar = $i;
+ $preStringBuffer = $buffer;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* looking for string closer *".PHP_EOL;
+ }
+ }//end if
+ }//end if
+
+ if ($inString !== '' && $char === "\n") {
+ // Unless this newline character is escaped, the string did not
+ // end before the end of the line, which means it probably
+ // wasn't a string at all (maybe a regex).
+ if ($chars[($i - 1)] !== '\\') {
+ $i = $stringChar;
+ $buffer = $preStringBuffer;
+ $preStringBuffer = '';
+ $inString = '';
+ $stringChar = null;
+ $char = $chars[$i];
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* found newline before end of string, bailing *".PHP_EOL;
+ }
+ }
+ }
+
+ $buffer .= $char;
+
+ // We don't look for special tokens inside strings,
+ // so if we are in a string, we can continue here now
+ // that the current char is in the buffer.
+ if ($inString !== '') {
+ continue;
+ }
+
+ // Special case for T_DIVIDE which can actually be
+ // the start of a regular expression.
+ if ($buffer === $char && $char === '/' && $chars[($i + 1)] !== '*') {
+ $regex = $this->getRegexToken(
+ $i,
+ $string,
+ $chars,
+ $tokens,
+ $this->eolChar
+ );
+
+ if ($regex !== null) {
+ $tokens[] = array(
+ 'code' => T_REGULAR_EXPRESSION,
+ 'type' => 'T_REGULAR_EXPRESSION',
+ 'content' => $regex['content'],
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($regex['content']);
+ echo "\t=> Added token T_REGULAR_EXPRESSION ($content)".PHP_EOL;
+ }
+
+ $i = $regex['end'];
+ $buffer = '';
+ $cleanBuffer = false;
+ continue;
+ }//end if
+ }//end if
+
+ // Check for known tokens, but ignore tokens found that are not at
+ // the end of a string, like FOR and this.FORmat.
+ if (isset($this->tokenValues[strtolower($buffer)]) === true
+ && (preg_match('|[a-zA-z0-9_]|', $char) === 0
+ || isset($chars[($i + 1)]) === false
+ || preg_match('|[a-zA-z0-9_]|', $chars[($i + 1)]) === 0)
+ ) {
+ $matchedToken = false;
+ $lookAheadLength = ($maxTokenLength - strlen($buffer));
+
+ if ($lookAheadLength > 0) {
+ // The buffer contains a token type, but we need
+ // to look ahead at the next chars to see if this is
+ // actually part of a larger token. For example,
+ // FOR and FOREACH.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* buffer possibly contains token, looking ahead $lookAheadLength chars *".PHP_EOL;
+ }
+
+ $charBuffer = $buffer;
+ for ($x = 1; $x <= $lookAheadLength; $x++) {
+ if (isset($chars[($i + $x)]) === false) {
+ break;
+ }
+
+ $charBuffer .= $chars[($i + $x)];
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($charBuffer);
+ echo "\t\t=> Looking ahead $x chars => $content".PHP_EOL;
+ }
+
+ if (isset($this->tokenValues[strtolower($charBuffer)]) === true) {
+ // We've found something larger that matches
+ // so we can ignore this char. Except for 1 very specific
+ // case where a comment like /**/ needs to tokenize as
+ // T_COMMENT and not T_DOC_COMMENT.
+ $oldType = $this->tokenValues[strtolower($buffer)];
+ $newType = $this->tokenValues[strtolower($charBuffer)];
+ if ($oldType === 'T_COMMENT'
+ && $newType === 'T_DOC_COMMENT'
+ && $chars[($i + $x + 1)] === '/'
+ ) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* look ahead ignored T_DOC_COMMENT, continuing *".PHP_EOL;
+ }
+ } else {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* look ahead found more specific token ($newType), ignoring $i *".PHP_EOL;
+ }
+
+ $matchedToken = true;
+ break;
+ }
+ }//end if
+ }//end for
+ }//end if
+
+ if ($matchedToken === false) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1 && $lookAheadLength > 0) {
+ echo "\t\t* look ahead found nothing *".PHP_EOL;
+ }
+
+ $value = $this->tokenValues[strtolower($buffer)];
+
+ if ($value === 'T_FUNCTION' && $buffer !== 'function') {
+ // The function keyword needs to be all lowercase or else
+ // it is just a function called "Function".
+ $value = 'T_STRING';
+ }
+
+ $tokens[] = array(
+ 'code' => constant($value),
+ 'type' => $value,
+ 'content' => $buffer,
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($buffer);
+ echo "\t=> Added token $value ($content)".PHP_EOL;
+ }
+
+ $cleanBuffer = true;
+ }//end if
+ } else if (isset($this->tokenValues[strtolower($char)]) === true) {
+ // No matter what token we end up using, we don't
+ // need the content in the buffer any more because we have
+ // found a valid token.
+ $newContent = substr(str_replace("\n", $this->eolChar, $buffer), 0, -1);
+ if ($newContent !== '') {
+ $tokens[] = array(
+ 'code' => T_STRING,
+ 'type' => 'T_STRING',
+ 'content' => $newContent,
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput(substr($buffer, 0, -1));
+ echo "\t=> Added token T_STRING ($content)".PHP_EOL;
+ }
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* char is token, looking ahead ".($maxTokenLength - 1).' chars *'.PHP_EOL;
+ }
+
+ // The char is a token type, but we need to look ahead at the
+ // next chars to see if this is actually part of a larger token.
+ // For example, = and ===.
+ $charBuffer = $char;
+ $matchedToken = false;
+ for ($x = 1; $x <= $maxTokenLength; $x++) {
+ if (isset($chars[($i + $x)]) === false) {
+ break;
+ }
+
+ $charBuffer .= $chars[($i + $x)];
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($charBuffer);
+ echo "\t\t=> Looking ahead $x chars => $content".PHP_EOL;
+ }
+
+ if (isset($this->tokenValues[strtolower($charBuffer)]) === true) {
+ // We've found something larger that matches
+ // so we can ignore this char.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokenValues[strtolower($charBuffer)];
+ echo "\t\t* look ahead found more specific token ($type), ignoring $i *".PHP_EOL;
+ }
+
+ $matchedToken = true;
+ break;
+ }
+ }//end for
+
+ if ($matchedToken === false) {
+ $value = $this->tokenValues[strtolower($char)];
+ $tokens[] = array(
+ 'code' => constant($value),
+ 'type' => $value,
+ 'content' => $char,
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* look ahead found nothing *".PHP_EOL;
+ $content = Util\Common::prepareForOutput($char);
+ echo "\t=> Added token $value ($content)".PHP_EOL;
+ }
+
+ $cleanBuffer = true;
+ } else {
+ $buffer = $char;
+ }//end if
+ }//end if
+
+ // Keep track of content inside comments.
+ if ($inComment === ''
+ && array_key_exists($buffer, $this->commentTokens) === true
+ ) {
+ // This is not really a comment if the content
+ // looks like \// (i.e., it is escaped).
+ if (isset($chars[($i - 2)]) === true && $chars[($i - 2)] === '\\') {
+ $lastToken = array_pop($tokens);
+ $lastContent = $lastToken['content'];
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $value = $this->tokenValues[strtolower($lastContent)];
+ $content = Util\Common::prepareForOutput($lastContent);
+ echo "\t=> Removed token $value ($content)".PHP_EOL;
+ }
+
+ $lastChars = str_split($lastContent);
+ $lastNumChars = count($lastChars);
+ for ($x = 0; $x < $lastNumChars; $x++) {
+ $lastChar = $lastChars[$x];
+ $value = $this->tokenValues[strtolower($lastChar)];
+ $tokens[] = array(
+ 'code' => constant($value),
+ 'type' => $value,
+ 'content' => $lastChar,
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($lastChar);
+ echo "\t=> Added token $value ($content)".PHP_EOL;
+ }
+ }
+ } else {
+ // We have started a comment.
+ $inComment = $buffer;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* looking for end of comment *".PHP_EOL;
+ }
+ }//end if
+ } else if ($inComment !== '') {
+ if ($this->commentTokens[$inComment] === null) {
+ // Comment ends at the next newline.
+ if (strpos($buffer, "\n") !== false) {
+ $inComment = '';
+ }
+ } else {
+ if ($this->commentTokens[$inComment] === $buffer) {
+ $inComment = '';
+ }
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ if ($inComment === '') {
+ echo "\t\t* found end of comment *".PHP_EOL;
+ }
+ }
+
+ if ($inComment === '' && $cleanBuffer === false) {
+ $tokens[] = array(
+ 'code' => T_STRING,
+ 'type' => 'T_STRING',
+ 'content' => str_replace("\n", $this->eolChar, $buffer),
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($buffer);
+ echo "\t=> Added token T_STRING ($content)".PHP_EOL;
+ }
+
+ $buffer = '';
+ }
+ }//end if
+
+ if ($cleanBuffer === true) {
+ $buffer = '';
+ $cleanBuffer = false;
+ }
+ }//end for
+
+ if (empty($buffer) === false) {
+ // Buffer contains whitespace from the end of the file.
+ $tokens[] = array(
+ 'code' => T_WHITESPACE,
+ 'type' => 'T_WHITESPACE',
+ 'content' => str_replace("\n", $this->eolChar, $buffer),
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $content = Util\Common::prepareForOutput($buffer);
+ echo "\t=> Added token T_WHITESPACE ($content)".PHP_EOL;
+ }
+ }
+
+ $tokens[] = array(
+ 'code' => T_CLOSE_TAG,
+ 'type' => 'T_CLOSE_TAG',
+ 'content' => '',
+ );
+
+ /*
+ Now that we have done some basic tokenizing, we need to
+ modify the tokens to join some together and split some apart
+ so they match what the PHP tokenizer does.
+ */
+
+ $finalTokens = array();
+ $newStackPtr = 0;
+ $numTokens = count($tokens);
+ for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) {
+ $token = $tokens[$stackPtr];
+
+ /*
+ Look for comments and join the tokens together.
+ */
+
+ if ($token['code'] === T_COMMENT || $token['code'] === T_DOC_COMMENT) {
+ $newContent = '';
+ $tokenContent = $token['content'];
+
+ $endContent = null;
+ if (isset($this->commentTokens[$tokenContent]) === true) {
+ $endContent = $this->commentTokens[$tokenContent];
+ }
+
+ while ($tokenContent !== $endContent) {
+ if ($endContent === null
+ && strpos($tokenContent, $this->eolChar) !== false
+ ) {
+ // A null end token means the comment ends at the end of
+ // the line so we look for newlines and split the token.
+ $tokens[$stackPtr]['content'] = substr(
+ $tokenContent,
+ (strpos($tokenContent, $this->eolChar) + strlen($this->eolChar))
+ );
+
+ $tokenContent = substr(
+ $tokenContent,
+ 0,
+ (strpos($tokenContent, $this->eolChar) + strlen($this->eolChar))
+ );
+
+ // If the substr failed, skip the token as the content
+ // will now be blank.
+ if ($tokens[$stackPtr]['content'] !== false
+ && $tokens[$stackPtr]['content'] !== ''
+ ) {
+ $stackPtr--;
+ }
+
+ break;
+ }//end if
+
+ $stackPtr++;
+ $newContent .= $tokenContent;
+ if (isset($tokens[$stackPtr]) === false) {
+ break;
+ }
+
+ $tokenContent = $tokens[$stackPtr]['content'];
+ }//end while
+
+ if ($token['code'] === T_DOC_COMMENT) {
+ $commentTokens = $commentTokenizer->tokenizeString($newContent.$tokenContent, $this->eolChar, $newStackPtr);
+ foreach ($commentTokens as $commentToken) {
+ $finalTokens[$newStackPtr] = $commentToken;
+ $newStackPtr++;
+ }
+
+ continue;
+ } else {
+ // Save the new content in the current token so
+ // the code below can chop it up on newlines.
+ $token['content'] = $newContent.$tokenContent;
+ }
+ }//end if
+
+ /*
+ If this token has newlines in its content, split each line up
+ and create a new token for each line. We do this so it's easier
+ to ascertain where errors occur on a line.
+ Note that $token[1] is the token's content.
+ */
+
+ if (strpos($token['content'], $this->eolChar) !== false) {
+ $tokenLines = explode($this->eolChar, $token['content']);
+ $numLines = count($tokenLines);
+
+ for ($i = 0; $i < $numLines; $i++) {
+ $newToken['content'] = $tokenLines[$i];
+ if ($i === ($numLines - 1)) {
+ if ($tokenLines[$i] === '') {
+ break;
+ }
+ } else {
+ $newToken['content'] .= $this->eolChar;
+ }
+
+ $newToken['type'] = $token['type'];
+ $newToken['code'] = $token['code'];
+ $finalTokens[$newStackPtr] = $newToken;
+ $newStackPtr++;
+ }
+ } else {
+ $finalTokens[$newStackPtr] = $token;
+ $newStackPtr++;
+ }//end if
+
+ // Convert numbers, including decimals.
+ if ($token['code'] === T_STRING
+ || $token['code'] === T_OBJECT_OPERATOR
+ ) {
+ $newContent = '';
+ $oldStackPtr = $stackPtr;
+ while (preg_match('|^[0-9\.]+$|', $tokens[$stackPtr]['content']) !== 0) {
+ $newContent .= $tokens[$stackPtr]['content'];
+ $stackPtr++;
+ }
+
+ if ($newContent !== '' && $newContent !== '.') {
+ $finalTokens[($newStackPtr - 1)]['content'] = $newContent;
+ if (ctype_digit($newContent) === true) {
+ $finalTokens[($newStackPtr - 1)]['code'] = constant('T_LNUMBER');
+ $finalTokens[($newStackPtr - 1)]['type'] = 'T_LNUMBER';
+ } else {
+ $finalTokens[($newStackPtr - 1)]['code'] = constant('T_DNUMBER');
+ $finalTokens[($newStackPtr - 1)]['type'] = 'T_DNUMBER';
+ }
+
+ $stackPtr--;
+ continue;
+ } else {
+ $stackPtr = $oldStackPtr;
+ }
+ }//end if
+
+ // Convert the token after an object operator into a string, in most cases.
+ if ($token['code'] === T_OBJECT_OPERATOR) {
+ for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
+ if (isset(Util\Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
+ continue;
+ }
+
+ if ($tokens[$i]['code'] !== T_PROTOTYPE
+ && $tokens[$i]['code'] !== T_LNUMBER
+ && $tokens[$i]['code'] !== T_DNUMBER
+ ) {
+ $tokens[$i]['code'] = T_STRING;
+ $tokens[$i]['type'] = 'T_STRING';
+ }
+
+ break;
+ }
+ }
+ }//end for
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** END TOKENIZING ***".PHP_EOL;
+ }
+
+ return $finalTokens;
+
+ }//end tokenize()
+
+
+ /**
+ * Tokenizes a regular expression if one is found.
+ *
+ * If a regular expression is not found, NULL is returned.
+ *
+ * @param string $char The index of the possible regex start character.
+ * @param string $string The complete content of the string being tokenized.
+ * @param string $chars An array of characters being tokenized.
+ * @param string $tokens The current array of tokens found in the string.
+ *
+ * @return void
+ */
+ public function getRegexToken($char, $string, $chars, $tokens)
+ {
+ $beforeTokens = array(
+ T_EQUAL => true,
+ T_IS_NOT_EQUAL => true,
+ T_IS_IDENTICAL => true,
+ T_IS_NOT_IDENTICAL => true,
+ T_OPEN_PARENTHESIS => true,
+ T_OPEN_SQUARE_BRACKET => true,
+ T_RETURN => true,
+ T_BOOLEAN_OR => true,
+ T_BOOLEAN_AND => true,
+ T_BITWISE_OR => true,
+ T_BITWISE_AND => true,
+ T_COMMA => true,
+ T_COLON => true,
+ T_TYPEOF => true,
+ T_INLINE_THEN => true,
+ T_INLINE_ELSE => true,
+ );
+
+ $afterTokens = array(
+ ',' => true,
+ ')' => true,
+ ']' => true,
+ ';' => true,
+ ' ' => true,
+ '.' => true,
+ ':' => true,
+ $this->eolChar => true,
+ );
+
+ // Find the last non-whitespace token that was added
+ // to the tokens array.
+ $numTokens = count($tokens);
+ for ($prev = ($numTokens - 1); $prev >= 0; $prev--) {
+ if (isset(Util\Tokens::$emptyTokens[$tokens[$prev]['code']]) === false) {
+ break;
+ }
+ }
+
+ if (isset($beforeTokens[$tokens[$prev]['code']]) === false) {
+ return null;
+ }
+
+ // This is probably a regular expression, so look for the end of it.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t* token possibly starts a regular expression *".PHP_EOL;
+ }
+
+ $numChars = count($chars);
+ for ($next = ($char + 1); $next < $numChars; $next++) {
+ if ($chars[$next] === '/') {
+ // Just make sure this is not escaped first.
+ if ($chars[($next - 1)] !== '\\') {
+ // In the simple form: /.../ so we found the end.
+ break;
+ } else if ($chars[($next - 2)] === '\\') {
+ // In the form: /...\\/ so we found the end.
+ break;
+ }
+ } else {
+ $possibleEolChar = substr($string, $next, strlen($this->eolChar));
+ if ($possibleEolChar === $this->eolChar) {
+ // This is the last token on the line and regular
+ // expressions need to be defined on a single line,
+ // so this is not a regular expression.
+ break;
+ }
+ }
+ }
+
+ if ($chars[$next] !== '/') {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t* could not find end of regular expression *".PHP_EOL;
+ }
+
+ return null;
+ }
+
+ while (preg_match('|[a-zA-Z]|', $chars[($next + 1)]) !== 0) {
+ // The token directly after the end of the regex can
+ // be modifiers like global and case insensitive
+ // (.e.g, /pattern/gi).
+ $next++;
+ }
+
+ $regexEnd = $next;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t* found end of regular expression at token $regexEnd *".PHP_EOL;
+ }
+
+ for ($next = ($next + 1); $next < $numChars; $next++) {
+ if ($chars[$next] !== ' ') {
+ break;
+ } else {
+ $possibleEolChar = substr($string, $next, strlen($this->eolChar));
+ if ($possibleEolChar === $this->eolChar) {
+ // This is the last token on the line.
+ break;
+ }
+ }
+ }
+
+ if (isset($afterTokens[$chars[$next]]) === false) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t* tokens after regular expression do not look correct *".PHP_EOL;
+ }
+
+ return null;
+ }
+
+ // This is a regular expression, so join all the tokens together.
+ $content = '';
+ for ($x = $char; $x <= $regexEnd; $x++) {
+ $content .= $chars[$x];
+ }
+
+ $token = array(
+ 'start' => $char,
+ 'end' => $regexEnd,
+ 'content' => $content,
+ );
+
+ return $token;
+
+ }//end getRegexToken()
+
+
+ /**
+ * Performs additional processing after main tokenizing.
+ *
+ * This additional processing looks for properties, closures, labels and objects.
+ *
+ * @return void
+ */
+ public function processAdditional()
+ {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** START ADDITIONAL JS PROCESSING ***".PHP_EOL;
+ }
+
+ $numTokens = count($this->tokens);
+ $classStack = array();
+
+ for ($i = 0; $i < $numTokens; $i++) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$i]['type'];
+ $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
+
+ echo str_repeat("\t", count($classStack));
+ echo "\tProcess token $i: $type => $content".PHP_EOL;
+ }
+
+ // Looking for functions that are actually closures.
+ if ($this->tokens[$i]['code'] === T_FUNCTION && isset($this->tokens[$i]['scope_opener']) === true) {
+ for ($x = ($i + 1); $x < $numTokens; $x++) {
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
+ break;
+ }
+ }
+
+ if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS) {
+ $this->tokens[$i]['code'] = T_CLOSURE;
+ $this->tokens[$i]['type'] = 'T_CLOSURE';
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $line = $this->tokens[$i]['line'];
+ echo str_repeat("\t", count($classStack));
+ echo "\t* token $i on line $line changed from T_FUNCTION to T_CLOSURE *".PHP_EOL;
+ }
+
+ for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) {
+ if (isset($this->tokens[$x]['conditions'][$i]) === false) {
+ continue;
+ }
+
+ $this->tokens[$x]['conditions'][$i] = T_CLOSURE;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$x]['type'];
+ echo str_repeat("\t", count($classStack));
+ echo "\t\t* cleaned $x ($type) *".PHP_EOL;
+ }
+ }
+ }//end if
+
+ continue;
+ } else if ($this->tokens[$i]['code'] === T_OPEN_CURLY_BRACKET
+ && isset($this->tokens[$i]['scope_condition']) === false
+ && isset($this->tokens[$i]['bracket_closer']) === true
+ ) {
+ $condition = end($this->tokens[$i]['conditions']);
+ reset($this->tokens[$i]['conditions']);
+ if ($condition === T_CLASS) {
+ // Possibly an ES6 method. To be classified as one, the previous
+ // non-empty tokens need to be a set of parenthesis, and then a string
+ // (the method name).
+ for ($parenCloser = ($i - 1); $parenCloser > 0; $parenCloser--) {
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$parenCloser]['code']]) === false) {
+ break;
+ }
+ }
+
+ if ($this->tokens[$parenCloser]['code'] === T_CLOSE_PARENTHESIS) {
+ $parenOpener = $this->tokens[$parenCloser]['parenthesis_opener'];
+ for ($name = ($parenOpener - 1); $name > 0; $name--) {
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$name]['code']]) === false) {
+ break;
+ }
+ }
+
+ if ($this->tokens[$name]['code'] === T_STRING) {
+ // We found a method name.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $line = $this->tokens[$name]['line'];
+ echo str_repeat("\t", count($classStack));
+ echo "\t* token $name on line $line changed from T_STRING to T_FUNCTION *".PHP_EOL;
+ }
+
+ $closer = $this->tokens[$i]['bracket_closer'];
+
+ $this->tokens[$name]['code'] = T_FUNCTION;
+ $this->tokens[$name]['type'] = 'T_FUNCTION';
+
+ foreach (array($name, $i, $closer) as $token) {
+ $this->tokens[$token]['scope_condition'] = $name;
+ $this->tokens[$token]['scope_opener'] = $i;
+ $this->tokens[$token]['scope_closer'] = $closer;
+ $this->tokens[$token]['parenthesis_opener'] = $parenOpener;
+ $this->tokens[$token]['parenthesis_closer'] = $parenCloser;
+ $this->tokens[$token]['parenthesis_owner'] = $name;
+ }
+
+ $this->tokens[$parenOpener]['parenthesis_owner'] = $name;
+ $this->tokens[$parenCloser]['parenthesis_owner'] = $name;
+
+ for ($x = ($i + 1); $x < $closer; $x++) {
+ $this->tokens[$x]['conditions'][$name] = T_FUNCTION;
+ ksort($this->tokens[$x]['conditions'], SORT_NUMERIC);
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$x]['type'];
+ echo str_repeat("\t", count($classStack));
+ echo "\t\t* added T_FUNCTION condition to $x ($type) *".PHP_EOL;
+ }
+ }
+
+ continue;
+ }//end if
+ }//end if
+ }//end if
+
+ $classStack[] = $i;
+
+ $closer = $this->tokens[$i]['bracket_closer'];
+ $this->tokens[$i]['code'] = T_OBJECT;
+ $this->tokens[$i]['type'] = 'T_OBJECT';
+ $this->tokens[$closer]['code'] = T_CLOSE_OBJECT;
+ $this->tokens[$closer]['type'] = 'T_CLOSE_OBJECT';
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", count($classStack));
+ echo "\t* token $i converted from T_OPEN_CURLY_BRACKET to T_OBJECT *".PHP_EOL;
+ echo str_repeat("\t", count($classStack));
+ echo "\t* token $closer converted from T_CLOSE_CURLY_BRACKET to T_CLOSE_OBJECT *".PHP_EOL;
+ }
+
+ for ($x = ($i + 1); $x < $closer; $x++) {
+ $this->tokens[$x]['conditions'][$i] = T_OBJECT;
+ ksort($this->tokens[$x]['conditions'], SORT_NUMERIC);
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$x]['type'];
+ echo str_repeat("\t", count($classStack));
+ echo "\t\t* added T_OBJECT condition to $x ($type) *".PHP_EOL;
+ }
+ }
+ } else if ($this->tokens[$i]['code'] === T_CLOSE_OBJECT) {
+ $opener = array_pop($classStack);
+ } else if ($this->tokens[$i]['code'] === T_COLON) {
+ // If it is a scope opener, it belongs to a
+ // DEFAULT or CASE statement.
+ if (isset($this->tokens[$i]['scope_condition']) === true) {
+ continue;
+ }
+
+ // Make sure this is not part of an inline IF statement.
+ for ($x = ($i - 1); $x >= 0; $x--) {
+ if ($this->tokens[$x]['code'] === T_INLINE_THEN) {
+ $this->tokens[$i]['code'] = T_INLINE_ELSE;
+ $this->tokens[$i]['type'] = 'T_INLINE_ELSE';
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", count($classStack));
+ echo "\t* token $i converted from T_COLON to T_INLINE_THEN *".PHP_EOL;
+ }
+
+ continue(2);
+ } else if ($this->tokens[$x]['line'] < $this->tokens[$i]['line']) {
+ break;
+ }
+ }
+
+ // The string to the left of the colon is either a property or label.
+ for ($label = ($i - 1); $label >= 0; $label--) {
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$label]['code']]) === false) {
+ break;
+ }
+ }
+
+ if ($this->tokens[$label]['code'] !== T_STRING
+ && $this->tokens[$label]['code'] !== T_CONSTANT_ENCAPSED_STRING
+ ) {
+ continue;
+ }
+
+ if (empty($classStack) === false) {
+ $this->tokens[$label]['code'] = T_PROPERTY;
+ $this->tokens[$label]['type'] = 'T_PROPERTY';
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", count($classStack));
+ echo "\t* token $label converted from T_STRING to T_PROPERTY *".PHP_EOL;
+ }
+ } else {
+ $this->tokens[$label]['code'] = T_LABEL;
+ $this->tokens[$label]['type'] = 'T_LABEL';
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", count($classStack));
+ echo "\t* token $label converted from T_STRING to T_LABEL *".PHP_EOL;
+ }
+ }//end if
+ }//end if
+ }//end for
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** END ADDITIONAL JS PROCESSING ***".PHP_EOL;
+ }
+
+ }//end processAdditional()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tokenizes PHP 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\Tokenizers;
+
+use PHP_CodeSniffer\Util;
+
+class PHP extends Tokenizer
+{
+
+
+ /**
+ * A list of tokens that are allowed to open a scope.
+ *
+ * This array also contains information about what kind of token the scope
+ * opener uses to open and close the scope, if the token strictly requires
+ * an opener, if the token can share a scope closer, and who it can be shared
+ * with. An example of a token that shares a scope closer is a CASE scope.
+ *
+ * @var array
+ */
+ public $scopeOpeners = array(
+ T_IF => array(
+ 'start' => array(
+ T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
+ T_COLON => T_COLON,
+ ),
+ 'end' => array(
+ T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
+ T_ENDIF => T_ENDIF,
+ T_ELSE => T_ELSE,
+ T_ELSEIF => T_ELSEIF,
+ ),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(
+ T_ELSE => T_ELSE,
+ T_ELSEIF => T_ELSEIF,
+ ),
+ ),
+ T_TRY => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_CATCH => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_FINALLY => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_ELSE => array(
+ 'start' => array(
+ T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
+ T_COLON => T_COLON,
+ ),
+ 'end' => array(
+ T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
+ T_ENDIF => T_ENDIF,
+ ),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(
+ T_IF => T_IF,
+ T_ELSEIF => T_ELSEIF,
+ ),
+ ),
+ T_ELSEIF => array(
+ 'start' => array(
+ T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
+ T_COLON => T_COLON,
+ ),
+ 'end' => array(
+ T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
+ T_ENDIF => T_ENDIF,
+ T_ELSE => T_ELSE,
+ T_ELSEIF => T_ELSEIF,
+ ),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(
+ T_IF => T_IF,
+ T_ELSE => T_ELSE,
+ ),
+ ),
+ T_FOR => array(
+ 'start' => array(
+ T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
+ T_COLON => T_COLON,
+ ),
+ 'end' => array(
+ T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
+ T_ENDFOR => T_ENDFOR,
+ ),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_FOREACH => array(
+ 'start' => array(
+ T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
+ T_COLON => T_COLON,
+ ),
+ 'end' => array(
+ T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
+ T_ENDFOREACH => T_ENDFOREACH,
+ ),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_INTERFACE => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_FUNCTION => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_CLASS => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_TRAIT => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_USE => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_DECLARE => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_NAMESPACE => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_WHILE => array(
+ 'start' => array(
+ T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
+ T_COLON => T_COLON,
+ ),
+ 'end' => array(
+ T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
+ T_ENDWHILE => T_ENDWHILE,
+ ),
+ 'strict' => false,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_DO => array(
+ 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET),
+ 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_SWITCH => array(
+ 'start' => array(
+ T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
+ T_COLON => T_COLON,
+ ),
+ 'end' => array(
+ T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
+ T_ENDSWITCH => T_ENDSWITCH,
+ ),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_CASE => array(
+ 'start' => array(
+ T_COLON => T_COLON,
+ T_SEMICOLON => T_SEMICOLON,
+ ),
+ 'end' => array(
+ T_BREAK => T_BREAK,
+ T_RETURN => T_RETURN,
+ T_CONTINUE => T_CONTINUE,
+ T_THROW => T_THROW,
+ T_EXIT => T_EXIT,
+ ),
+ 'strict' => true,
+ 'shared' => true,
+ 'with' => array(
+ T_DEFAULT => T_DEFAULT,
+ T_CASE => T_CASE,
+ T_SWITCH => T_SWITCH,
+ ),
+ ),
+ T_DEFAULT => array(
+ 'start' => array(
+ T_COLON => T_COLON,
+ T_SEMICOLON => T_SEMICOLON,
+ ),
+ 'end' => array(
+ T_BREAK => T_BREAK,
+ T_RETURN => T_RETURN,
+ T_CONTINUE => T_CONTINUE,
+ T_THROW => T_THROW,
+ T_EXIT => T_EXIT,
+ ),
+ 'strict' => true,
+ 'shared' => true,
+ 'with' => array(
+ T_CASE => T_CASE,
+ T_SWITCH => T_SWITCH,
+ ),
+ ),
+ T_START_HEREDOC => array(
+ 'start' => array(T_START_HEREDOC => T_START_HEREDOC),
+ 'end' => array(T_END_HEREDOC => T_END_HEREDOC),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ T_START_NOWDOC => array(
+ 'start' => array(T_START_NOWDOC => T_START_NOWDOC),
+ 'end' => array(T_END_NOWDOC => T_END_NOWDOC),
+ 'strict' => true,
+ 'shared' => false,
+ 'with' => array(),
+ ),
+ );
+
+ /**
+ * A list of tokens that end the scope.
+ *
+ * This array is just a unique collection of the end tokens
+ * from the _scopeOpeners array. The data is duplicated here to
+ * save time during parsing of the file.
+ *
+ * @var array
+ */
+ public $endScopeTokens = array(
+ T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
+ T_ENDIF => T_ENDIF,
+ T_ENDFOR => T_ENDFOR,
+ T_ENDFOREACH => T_ENDFOREACH,
+ T_ENDWHILE => T_ENDWHILE,
+ T_ENDSWITCH => T_ENDSWITCH,
+ T_BREAK => T_BREAK,
+ T_END_HEREDOC => T_END_HEREDOC,
+ );
+
+ /**
+ * Known lengths of tokens.
+ *
+ * @var array<int, int>
+ */
+ public $knownLengths = array(
+ T_ABSTRACT => 8,
+ T_AND_EQUAL => 2,
+ T_ARRAY => 5,
+ T_AS => 2,
+ T_BOOLEAN_AND => 2,
+ T_BOOLEAN_OR => 2,
+ T_BREAK => 5,
+ T_CALLABLE => 8,
+ T_CASE => 4,
+ T_CATCH => 5,
+ T_CLASS => 5,
+ T_CLASS_C => 9,
+ T_CLONE => 5,
+ T_CONCAT_EQUAL => 2,
+ T_CONST => 5,
+ T_CONTINUE => 8,
+ T_CURLY_OPEN => 2,
+ T_DEC => 2,
+ T_DECLARE => 7,
+ T_DEFAULT => 7,
+ T_DIR => 7,
+ T_DIV_EQUAL => 2,
+ T_DO => 2,
+ T_DOLLAR_OPEN_CURLY_BRACES => 2,
+ T_DOUBLE_ARROW => 2,
+ T_DOUBLE_COLON => 2,
+ T_ECHO => 4,
+ T_ELSE => 4,
+ T_ELSEIF => 6,
+ T_EMPTY => 5,
+ T_ENDDECLARE => 10,
+ T_ENDFOR => 6,
+ T_ENDFOREACH => 10,
+ T_ENDIF => 5,
+ T_ENDSWITCH => 9,
+ T_ENDWHILE => 8,
+ T_EVAL => 4,
+ T_EXTENDS => 7,
+ T_FILE => 8,
+ T_FINAL => 5,
+ T_FINALLY => 7,
+ T_FOR => 3,
+ T_FOREACH => 7,
+ T_FUNCTION => 8,
+ T_FUNC_C => 12,
+ T_GLOBAL => 6,
+ T_GOTO => 4,
+ T_HALT_COMPILER => 15,
+ T_IF => 2,
+ T_IMPLEMENTS => 10,
+ T_INC => 2,
+ T_INCLUDE => 7,
+ T_INCLUDE_ONCE => 12,
+ T_INSTANCEOF => 10,
+ T_INSTEADOF => 9,
+ T_INTERFACE => 9,
+ T_ISSET => 5,
+ T_IS_EQUAL => 2,
+ T_IS_GREATER_OR_EQUAL => 2,
+ T_IS_IDENTICAL => 3,
+ T_IS_NOT_EQUAL => 2,
+ T_IS_NOT_IDENTICAL => 3,
+ T_IS_SMALLER_OR_EQUAL => 2,
+ T_LINE => 8,
+ T_LIST => 4,
+ T_LOGICAL_AND => 3,
+ T_LOGICAL_OR => 2,
+ T_LOGICAL_XOR => 3,
+ T_METHOD_C => 10,
+ T_MINUS_EQUAL => 2,
+ T_POW_EQUAL => 3,
+ T_MOD_EQUAL => 2,
+ T_MUL_EQUAL => 2,
+ T_NAMESPACE => 9,
+ T_NS_C => 13,
+ T_NS_SEPARATOR => 1,
+ T_NEW => 3,
+ T_OBJECT_OPERATOR => 2,
+ T_OPEN_TAG_WITH_ECHO => 3,
+ T_OR_EQUAL => 2,
+ T_PLUS_EQUAL => 2,
+ T_PRINT => 5,
+ T_PRIVATE => 7,
+ T_PUBLIC => 6,
+ T_PROTECTED => 9,
+ T_REQUIRE => 7,
+ T_REQUIRE_ONCE => 12,
+ T_RETURN => 6,
+ T_STATIC => 6,
+ T_SWITCH => 6,
+ T_THROW => 5,
+ T_TRAIT => 5,
+ T_TRAIT_C => 9,
+ T_TRY => 3,
+ T_UNSET => 5,
+ T_USE => 3,
+ T_VAR => 3,
+ T_WHILE => 5,
+ T_XOR_EQUAL => 2,
+ T_YIELD => 5,
+ T_OPEN_CURLY_BRACKET => 1,
+ T_CLOSE_CURLY_BRACKET => 1,
+ T_OPEN_SQUARE_BRACKET => 1,
+ T_CLOSE_SQUARE_BRACKET => 1,
+ T_OPEN_PARENTHESIS => 1,
+ T_CLOSE_PARENTHESIS => 1,
+ T_COLON => 1,
+ T_STRING_CONCAT => 1,
+ T_INLINE_THEN => 1,
+ T_INLINE_ELSE => 1,
+ T_NULLABLE => 1,
+ T_NULL => 4,
+ T_FALSE => 5,
+ T_TRUE => 4,
+ T_SEMICOLON => 1,
+ T_EQUAL => 1,
+ T_MULTIPLY => 1,
+ T_DIVIDE => 1,
+ T_PLUS => 1,
+ T_MINUS => 1,
+ T_MODULUS => 1,
+ T_POW => 2,
+ T_SPACESHIP => 3,
+ T_COALESCE => 2,
+ T_COALESCE_EQUAL => 3,
+ T_BITWISE_AND => 1,
+ T_BITWISE_OR => 1,
+ T_BITWISE_XOR => 1,
+ T_SL => 2,
+ T_SR => 2,
+ T_SL_EQUAL => 3,
+ T_SR_EQUAL => 3,
+ T_ARRAY_HINT => 5,
+ T_GREATER_THAN => 1,
+ T_LESS_THAN => 1,
+ T_BOOLEAN_NOT => 1,
+ T_SELF => 4,
+ T_PARENT => 6,
+ T_COMMA => 1,
+ T_THIS => 4,
+ T_CLOSURE => 8,
+ T_BACKTICK => 1,
+ T_OPEN_SHORT_ARRAY => 1,
+ T_CLOSE_SHORT_ARRAY => 1,
+ );
+
+
+ /**
+ * A cache of different token types, resolved into arrays.
+ *
+ * @var array
+ * @see standardiseToken()
+ */
+ private static $resolveTokenCache = array();
+
+
+ /**
+ * Creates an array of tokens when given some PHP code.
+ *
+ * Starts by using token_get_all() but does a lot of extra processing
+ * to insert information about the context of the token.
+ *
+ * @param string $string The string to tokenize.
+ *
+ * @return array
+ */
+ protected function tokenize($string)
+ {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** START PHP TOKENIZING ***".PHP_EOL;
+ $isWin = false;
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+ $isWin = true;
+ }
+ }
+
+ $tokens = @token_get_all($string);
+ $finalTokens = array();
+
+ $newStackPtr = 0;
+ $numTokens = count($tokens);
+ $lastNotEmptyToken = 0;
+
+ $insideInlineIf = array();
+ $insideUseGroup = false;
+
+ $commentTokenizer = new Comment();
+
+ for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) {
+ $token = (array) $tokens[$stackPtr];
+ $tokenIsArray = isset($token[1]);
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ if ($tokenIsArray === true) {
+ $type = Util\Tokens::tokenName($token[0]);
+ $content = Util\Common::prepareForOutput($token[1]);
+ } else {
+ $newToken = self::resolveSimpleToken($token[0]);
+ $type = $newToken['type'];
+ $content = Util\Common::prepareForOutput($token[0]);
+ }
+
+ echo "\tProcess token ";
+ if ($tokenIsArray === true) {
+ echo "[$stackPtr]";
+ } else {
+ echo " $stackPtr ";
+ }
+
+ echo ": $type => $content";
+ }//end if
+
+ if ($newStackPtr > 0 && $finalTokens[($newStackPtr - 1)]['code'] !== T_WHITESPACE) {
+ $lastNotEmptyToken = ($newStackPtr - 1);
+ }
+
+ /*
+ If we are using \r\n newline characters, the \r and \n are sometimes
+ split over two tokens. This normally occurs after comments. We need
+ to merge these two characters together so that our line endings are
+ consistent for all lines.
+ */
+
+ if ($tokenIsArray === true && substr($token[1], -1) === "\r") {
+ if (isset($tokens[($stackPtr + 1)]) === true
+ && is_array($tokens[($stackPtr + 1)]) === true
+ && $tokens[($stackPtr + 1)][1][0] === "\n"
+ ) {
+ $token[1] .= "\n";
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ if ($isWin === true) {
+ echo '\n';
+ } else {
+ echo "\033[30;1m\\n\033[0m";
+ }
+ }
+
+ if ($tokens[($stackPtr + 1)][1] === "\n") {
+ // This token's content has been merged into the previous,
+ // so we can skip it.
+ $tokens[($stackPtr + 1)] = '';
+ } else {
+ $tokens[($stackPtr + 1)][1] = substr($tokens[($stackPtr + 1)][1], 1);
+ }
+ }
+ }//end if
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo PHP_EOL;
+ }
+
+ /*
+ Parse doc blocks into something that can be easily iterated over.
+ */
+
+ if ($tokenIsArray === true
+ && ($token[0] === T_DOC_COMMENT
+ || ($token[0] === T_COMMENT && strpos($token[1], '/**') === 0))
+ ) {
+ $commentTokens = $commentTokenizer->tokenizeString($token[1], $this->eolChar, $newStackPtr);
+ foreach ($commentTokens as $commentToken) {
+ $finalTokens[$newStackPtr] = $commentToken;
+ $newStackPtr++;
+ }
+
+ continue;
+ }
+
+ /*
+ If this is a double quoted string, PHP will tokenize the whole
+ thing which causes problems with the scope map when braces are
+ within the string. So we need to merge the tokens together to
+ provide a single string.
+ */
+
+ if ($tokenIsArray === false && ($token[0] === '"' || $token[0] === 'b"')) {
+ // Binary casts need a special token.
+ if ($token[0] === 'b"') {
+ $finalTokens[$newStackPtr] = array(
+ 'code' => T_BINARY_CAST,
+ 'type' => 'T_BINARY_CAST',
+ 'content' => 'b',
+ );
+ $newStackPtr++;
+ }
+
+ $tokenContent = '"';
+ $nestedVars = array();
+ for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
+ $subToken = (array) $tokens[$i];
+ $subTokenIsArray = isset($subToken[1]);
+
+ if ($subTokenIsArray === true) {
+ $tokenContent .= $subToken[1];
+ if ($subToken[1] === '{'
+ && $subToken[0] !== T_ENCAPSED_AND_WHITESPACE
+ ) {
+ $nestedVars[] = $i;
+ }
+ } else {
+ $tokenContent .= $subToken[0];
+ if ($subToken[0] === '}') {
+ array_pop($nestedVars);
+ }
+ }
+
+ if ($subTokenIsArray === false
+ && $subToken[0] === '"'
+ && empty($nestedVars) === true
+ ) {
+ // We found the other end of the double quoted string.
+ break;
+ }
+ }//end for
+
+ $stackPtr = $i;
+
+ // Convert each line within the double quoted string to a
+ // new token, so it conforms with other multiple line tokens.
+ $tokenLines = explode($this->eolChar, $tokenContent);
+ $numLines = count($tokenLines);
+ $newToken = array();
+
+ for ($j = 0; $j < $numLines; $j++) {
+ $newToken['content'] = $tokenLines[$j];
+ if ($j === ($numLines - 1)) {
+ if ($tokenLines[$j] === '') {
+ break;
+ }
+ } else {
+ $newToken['content'] .= $this->eolChar;
+ }
+
+ $newToken['code'] = T_DOUBLE_QUOTED_STRING;
+ $newToken['type'] = 'T_DOUBLE_QUOTED_STRING';
+ $finalTokens[$newStackPtr] = $newToken;
+ $newStackPtr++;
+ }
+
+ // Continue, as we're done with this token.
+ continue;
+ }//end if
+
+ /*
+ If this is a heredoc, PHP will tokenize the whole
+ thing which causes problems when heredocs don't
+ contain real PHP code, which is almost never.
+ We want to leave the start and end heredoc tokens
+ alone though.
+ */
+
+ if ($tokenIsArray === true && $token[0] === T_START_HEREDOC) {
+ // Add the start heredoc token to the final array.
+ $finalTokens[$newStackPtr] = self::standardiseToken($token);
+
+ // Check if this is actually a nowdoc and use a different token
+ // to help the sniffs.
+ $nowdoc = false;
+ if ($token[1][3] === "'") {
+ $finalTokens[$newStackPtr]['code'] = T_START_NOWDOC;
+ $finalTokens[$newStackPtr]['type'] = 'T_START_NOWDOC';
+ $nowdoc = true;
+ }
+
+ $tokenContent = '';
+ for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
+ $subTokenIsArray = is_array($tokens[$i]);
+ if ($subTokenIsArray === true
+ && $tokens[$i][0] === T_END_HEREDOC
+ ) {
+ // We found the other end of the heredoc.
+ break;
+ }
+
+ if ($subTokenIsArray === true) {
+ $tokenContent .= $tokens[$i][1];
+ } else {
+ $tokenContent .= $tokens[$i];
+ }
+ }
+
+ if ($i === $numTokens) {
+ // We got to the end of the file and never
+ // found the closing token, so this probably wasn't
+ // a heredoc.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $finalTokens[$newStackPtr]['type'];
+ echo "\t\t* failed to find the end of the here/nowdoc".PHP_EOL;
+ echo "\t\t* token $stackPtr changed from $type to T_STRING".PHP_EOL;
+ }
+
+ $finalTokens[$newStackPtr]['code'] = T_STRING;
+ $finalTokens[$newStackPtr]['type'] = 'T_STRING';
+ $newStackPtr++;
+ continue;
+ }
+
+ $stackPtr = $i;
+ $newStackPtr++;
+
+ // Convert each line within the heredoc to a
+ // new token, so it conforms with other multiple line tokens.
+ $tokenLines = explode($this->eolChar, $tokenContent);
+ $numLines = count($tokenLines);
+ $newToken = array();
+
+ for ($j = 0; $j < $numLines; $j++) {
+ $newToken['content'] = $tokenLines[$j];
+ if ($j === ($numLines - 1)) {
+ if ($tokenLines[$j] === '') {
+ break;
+ }
+ } else {
+ $newToken['content'] .= $this->eolChar;
+ }
+
+ if ($nowdoc === true) {
+ $newToken['code'] = T_NOWDOC;
+ $newToken['type'] = 'T_NOWDOC';
+ } else {
+ $newToken['code'] = T_HEREDOC;
+ $newToken['type'] = 'T_HEREDOC';
+ }
+
+ $finalTokens[$newStackPtr] = $newToken;
+ $newStackPtr++;
+ }//end for
+
+ // Add the end heredoc token to the final array.
+ $finalTokens[$newStackPtr] = self::standardiseToken($tokens[$stackPtr]);
+
+ if ($nowdoc === true) {
+ $finalTokens[$newStackPtr]['code'] = T_END_NOWDOC;
+ $finalTokens[$newStackPtr]['type'] = 'T_END_NOWDOC';
+ $nowdoc = true;
+ }
+
+ $newStackPtr++;
+
+ // Continue, as we're done with this token.
+ continue;
+ }//end if
+
+ /*
+ Before PHP 7.0, the "yield from" was tokenized as
+ T_YIELD, T_WHITESPACE and T_STRING. So look for
+ and change this token in earlier versions.
+ */
+
+ if (PHP_VERSION_ID < 70000
+ && PHP_VERSION_ID >= 50500
+ && $tokenIsArray === true
+ && $token[0] === T_YIELD
+ && isset($tokens[($stackPtr + 1)]) === true
+ && isset($tokens[($stackPtr + 2)]) === true
+ && $tokens[($stackPtr + 1)][0] === T_WHITESPACE
+ && $tokens[($stackPtr + 2)][0] === T_STRING
+ && strtolower($tokens[($stackPtr + 2)][1]) === 'from'
+ ) {
+ $newToken = array();
+ $newToken['code'] = T_YIELD_FROM;
+ $newToken['type'] = 'T_YIELD_FROM';
+ $newToken['content'] = $token[1].$tokens[($stackPtr + 1)][1].$tokens[($stackPtr + 2)][1];
+ $finalTokens[$newStackPtr] = $newToken;
+
+ $newStackPtr++;
+ $stackPtr += 2;
+ continue;
+ }
+
+ /*
+ Before PHP 5.5, the yield keyword was tokenized as
+ T_STRING. So look for and change this token in
+ earlier versions.
+ Checks also if it is just "yield" or "yield from".
+ */
+
+ if (PHP_VERSION_ID < 50500
+ && $tokenIsArray === true
+ && $token[0] === T_STRING
+ && strtolower($token[1]) === 'yield'
+ ) {
+ if (isset($tokens[($stackPtr + 1)]) === true
+ && isset($tokens[($stackPtr + 2)]) === true
+ && $tokens[($stackPtr + 1)][0] === T_WHITESPACE
+ && $tokens[($stackPtr + 2)][0] === T_STRING
+ && strtolower($tokens[($stackPtr + 2)][1]) === 'from'
+ ) {
+ $newToken = array();
+ $newToken['code'] = T_YIELD_FROM;
+ $newToken['type'] = 'T_YIELD_FROM';
+ $newToken['content'] = $token[1].$tokens[($stackPtr + 1)][1].$tokens[($stackPtr + 2)][1];
+ $finalTokens[$newStackPtr] = $newToken;
+
+ $newStackPtr++;
+ $stackPtr += 2;
+ continue;
+ }
+
+ $newToken = array();
+ $newToken['code'] = T_YIELD;
+ $newToken['type'] = 'T_YIELD';
+ $newToken['content'] = $token[1];
+ $finalTokens[$newStackPtr] = $newToken;
+
+ $newStackPtr++;
+ continue;
+ }//end if
+
+ /*
+ Before PHP 5.6, the ... operator was tokenized as three
+ T_STRING_CONCAT tokens in a row. So look for and combine
+ these tokens in earlier versions.
+ */
+
+ if ($tokenIsArray === false
+ && $token[0] === '.'
+ && isset($tokens[($stackPtr + 1)]) === true
+ && isset($tokens[($stackPtr + 2)]) === true
+ && $tokens[($stackPtr + 1)] === '.'
+ && $tokens[($stackPtr + 2)] === '.'
+ ) {
+ $newToken = array();
+ $newToken['code'] = T_ELLIPSIS;
+ $newToken['type'] = 'T_ELLIPSIS';
+ $newToken['content'] = '...';
+ $finalTokens[$newStackPtr] = $newToken;
+
+ $newStackPtr++;
+ $stackPtr += 2;
+ continue;
+ }
+
+ /*
+ Before PHP 5.6, the ** operator was tokenized as two
+ T_MULTIPLY tokens in a row. So look for and combine
+ these tokens in earlier versions.
+ */
+
+ if ($tokenIsArray === false
+ && $token[0] === '*'
+ && isset($tokens[($stackPtr + 1)]) === true
+ && $tokens[($stackPtr + 1)] === '*'
+ ) {
+ $newToken = array();
+ $newToken['code'] = T_POW;
+ $newToken['type'] = 'T_POW';
+ $newToken['content'] = '**';
+ $finalTokens[$newStackPtr] = $newToken;
+
+ $newStackPtr++;
+ $stackPtr++;
+ continue;
+ }
+
+ /*
+ Before PHP 5.6, the **= operator was tokenized as
+ T_MULTIPLY followed by T_MUL_EQUAL. So look for and combine
+ these tokens in earlier versions.
+ */
+
+ if ($tokenIsArray === false
+ && $token[0] === '*'
+ && isset($tokens[($stackPtr + 1)]) === true
+ && is_array($tokens[($stackPtr + 1)]) === true
+ && $tokens[($stackPtr + 1)][1] === '*='
+ ) {
+ $newToken = array();
+ $newToken['code'] = T_POW_EQUAL;
+ $newToken['type'] = 'T_POW_EQUAL';
+ $newToken['content'] = '**=';
+ $finalTokens[$newStackPtr] = $newToken;
+
+ $newStackPtr++;
+ $stackPtr++;
+ continue;
+ }
+
+ /*
+ Before PHP 7, the ??= operator was tokenized as
+ T_INLINE_THEN, T_INLINE_THEN, T_EQUAL.
+ Between PHP 7.0 and 7.2, the ??= operator was tokenized as
+ T_COALESCE, T_EQUAL.
+ So look for and combine these tokens in earlier versions.
+ */
+
+ if (($tokenIsArray === false
+ && $token[0] === '?'
+ && isset($tokens[($stackPtr + 1)]) === true
+ && $tokens[($stackPtr + 1)][0] === '?'
+ && isset($tokens[($stackPtr + 2)]) === true
+ && $tokens[($stackPtr + 2)][0] === '=')
+ || ($tokenIsArray === true
+ && $token[0] === T_COALESCE
+ && isset($tokens[($stackPtr + 1)]) === true
+ && $tokens[($stackPtr + 1)][0] === '=')
+ ) {
+ $newToken = array();
+ $newToken['code'] = T_COALESCE_EQUAL;
+ $newToken['type'] = 'T_COALESCE_EQUAL';
+ $newToken['content'] = '??=';
+ $finalTokens[$newStackPtr] = $newToken;
+
+ $newStackPtr++;
+ $stackPtr++;
+
+ if ($tokenIsArray === false) {
+ // Pre PHP 7.
+ $stackPtr++;
+ }
+
+ continue;
+ }
+
+ /*
+ Before PHP 7, the ?? operator was tokenized as
+ T_INLINE_THEN followed by T_INLINE_THEN.
+ So look for and combine these tokens in earlier versions.
+ */
+
+ if ($tokenIsArray === false
+ && $token[0] === '?'
+ && isset($tokens[($stackPtr + 1)]) === true
+ && $tokens[($stackPtr + 1)][0] === '?'
+ ) {
+ $newToken = array();
+ $newToken['code'] = T_COALESCE;
+ $newToken['type'] = 'T_COALESCE';
+ $newToken['content'] = '??';
+ $finalTokens[$newStackPtr] = $newToken;
+
+ $newStackPtr++;
+ $stackPtr++;
+ continue;
+ }
+
+ /*
+ Convert ? to T_NULLABLE OR T_INLINE_THEN
+ */
+
+ if ($tokenIsArray === false && $token[0] === '?') {
+ $newToken = array();
+ $newToken['content'] = '?';
+
+ $prevNonEmpty = null;
+ for ($i = ($stackPtr - 1); $i >= 0; $i--) {
+ if (is_array($tokens[$i]) === true) {
+ $tokenType = $tokens[$i][0];
+ } else {
+ $tokenType = $tokens[$i];
+ }
+
+ if ($prevNonEmpty === null
+ && isset(Util\Tokens::$emptyTokens[$tokenType]) === false
+ ) {
+ // Found the previous non-empty token.
+ if ($tokenType === ':' || $tokenType === ',') {
+ $newToken['code'] = T_NULLABLE;
+ $newToken['type'] = 'T_NULLABLE';
+ break;
+ }
+
+ $prevNonEmpty = $tokenType;
+ }
+
+ if ($tokenType === T_FUNCTION) {
+ $newToken['code'] = T_NULLABLE;
+ $newToken['type'] = 'T_NULLABLE';
+ break;
+ } else if (in_array($tokenType, array(T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, '=', '{', ';')) === true) {
+ $newToken['code'] = T_INLINE_THEN;
+ $newToken['type'] = 'T_INLINE_THEN';
+
+ $insideInlineIf[] = $stackPtr;
+ break;
+ }
+ }//end for
+
+ $finalTokens[$newStackPtr] = $newToken;
+ $newStackPtr++;
+ continue;
+ }//end if
+
+ /*
+ Tokens after a double colon may be look like scope openers,
+ such as when writing code like Foo::NAMESPACE, but they are
+ only ever variables or strings.
+ */
+
+ if ($stackPtr > 1
+ && (is_array($tokens[($stackPtr - 1)]) === true
+ && $tokens[($stackPtr - 1)][0] === T_PAAMAYIM_NEKUDOTAYIM)
+ && $tokenIsArray === true
+ && $token[0] !== T_STRING
+ && $token[0] !== T_VARIABLE
+ && $token[0] !== T_DOLLAR
+ && isset(Util\Tokens::$emptyTokens[$token[0]]) === false
+ ) {
+ $newToken = array();
+ $newToken['code'] = T_STRING;
+ $newToken['type'] = 'T_STRING';
+ $newToken['content'] = $token[1];
+ $finalTokens[$newStackPtr] = $newToken;
+
+ $newStackPtr++;
+ continue;
+ }
+
+ /*
+ The string-like token after a function keyword should always be
+ tokenized as T_STRING even if it appears to be a different token,
+ such as when writing code like: function default(): foo
+ so go forward and change the token type before it is processed.
+ */
+
+ if ($tokenIsArray === true && $token[0] === T_FUNCTION
+ && $finalTokens[$lastNotEmptyToken]['code'] !== T_USE
+ ) {
+ for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
+ if (is_array($tokens[$x]) === false
+ || isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === false
+ ) {
+ // Non-empty content.
+ break;
+ }
+ }
+
+ if ($x < $numTokens && is_array($tokens[$x]) === true) {
+ $tokens[$x][0] = T_STRING;
+ }
+ }
+
+ /*
+ Before PHP 7, the <=> operator was tokenized as
+ T_IS_SMALLER_OR_EQUAL followed by T_GREATER_THAN.
+ So look for and combine these tokens in earlier versions.
+ */
+
+ if ($tokenIsArray === true
+ && $token[0] === T_IS_SMALLER_OR_EQUAL
+ && isset($tokens[($stackPtr + 1)]) === true
+ && $tokens[($stackPtr + 1)][0] === '>'
+ ) {
+ $newToken = array();
+ $newToken['code'] = T_SPACESHIP;
+ $newToken['type'] = 'T_SPACESHIP';
+ $newToken['content'] = '<=>';
+ $finalTokens[$newStackPtr] = $newToken;
+
+ $newStackPtr++;
+ $stackPtr++;
+ continue;
+ }
+
+ /*
+ PHP doesn't assign a token to goto labels, so we have to.
+ These are just string tokens with a single colon after them. Double
+ colons are already tokenized and so don't interfere with this check.
+ But we do have to account for CASE statements, that look just like
+ goto labels.
+ */
+
+ if ($tokenIsArray === true
+ && $token[0] === T_STRING
+ && isset($tokens[($stackPtr + 1)]) === true
+ && $tokens[($stackPtr + 1)] === ':'
+ && $tokens[($stackPtr - 1)][0] !== T_PAAMAYIM_NEKUDOTAYIM
+ ) {
+ $stopTokens = array(
+ T_CASE => true,
+ T_SEMICOLON => true,
+ T_OPEN_CURLY_BRACKET => true,
+ T_INLINE_THEN => true,
+ );
+
+ for ($x = ($newStackPtr - 1); $x > 0; $x--) {
+ if (isset($stopTokens[$finalTokens[$x]['code']]) === true) {
+ break;
+ }
+ }
+
+ if ($finalTokens[$x]['code'] !== T_CASE
+ && $finalTokens[$x]['code'] !== T_INLINE_THEN
+ ) {
+ $finalTokens[$newStackPtr] = array(
+ 'content' => $token[1].':',
+ 'code' => T_GOTO_LABEL,
+ 'type' => 'T_GOTO_LABEL',
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* token $stackPtr changed from T_STRING to T_GOTO_LABEL".PHP_EOL;
+ echo "\t\t* skipping T_COLON token ".($stackPtr + 1).PHP_EOL;
+ }
+
+ $newStackPtr++;
+ $stackPtr++;
+ continue;
+ }
+ }//end if
+
+ /*
+ HHVM 3.5 tokenizes "else[\s]+if" as a T_ELSEIF token while PHP
+ proper only tokenizes "elseif" as a T_ELSEIF token. So split
+ up the HHVM token to make it looks like proper PHP.
+ */
+
+ if ($tokenIsArray === true
+ && $token[0] === T_ELSEIF
+ && strtolower($token[1]) !== 'elseif'
+ ) {
+ $finalTokens[$newStackPtr] = array(
+ 'content' => substr($token[1], 0, 4),
+ 'code' => T_ELSE,
+ 'type' => 'T_ELSE',
+ );
+
+ $newStackPtr++;
+ $finalTokens[$newStackPtr] = array(
+ 'content' => substr($token[1], 4, -2),
+ 'code' => T_WHITESPACE,
+ 'type' => 'T_WHITESPACE',
+ );
+
+ $newStackPtr++;
+ $finalTokens[$newStackPtr] = array(
+ 'content' => substr($token[1], -2),
+ 'code' => T_IF,
+ 'type' => 'T_IF',
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* token $stackPtr changed from T_ELSEIF to T_ELSE/T_WHITESPACE/T_IF".PHP_EOL;
+ }
+
+ $newStackPtr++;
+ continue;
+ }//end if
+
+ /*
+ HHVM 3.5 and 3.6 tokenizes a hashbang line such as #!/usr/bin/php
+ as T_HASHBANG while PHP proper uses T_INLINE_HTML.
+ */
+
+ if ($tokenIsArray === true && token_name($token[0]) === 'T_HASHBANG') {
+ $finalTokens[$newStackPtr] = array(
+ 'content' => $token[1],
+ 'code' => T_INLINE_HTML,
+ 'type' => 'T_INLINE_HTML',
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* token $stackPtr changed from T_HASHBANG to T_INLINE_HTML".PHP_EOL;
+ }
+
+ $newStackPtr++;
+ continue;
+ }//end if
+
+ /*
+ If this token has newlines in its content, split each line up
+ and create a new token for each line. We do this so it's easier
+ to ascertain where errors occur on a line.
+ Note that $token[1] is the token's content.
+ */
+
+ if ($tokenIsArray === true && strpos($token[1], $this->eolChar) !== false) {
+ $tokenLines = explode($this->eolChar, $token[1]);
+ $numLines = count($tokenLines);
+ $newToken = array(
+ 'type' => token_name($token[0]),
+ 'code' => $token[0],
+ 'content' => '',
+ );
+
+ for ($i = 0; $i < $numLines; $i++) {
+ $newToken['content'] = $tokenLines[$i];
+ if ($i === ($numLines - 1)) {
+ if ($tokenLines[$i] === '') {
+ break;
+ }
+ } else {
+ $newToken['content'] .= $this->eolChar;
+ }
+
+ $finalTokens[$newStackPtr] = $newToken;
+ $newStackPtr++;
+ }
+ } else {
+ if ($tokenIsArray === true && $token[0] === T_STRING) {
+ // Some T_STRING tokens should remain that way
+ // due to their context.
+ $context = array(
+ T_OBJECT_OPERATOR => true,
+ T_FUNCTION => true,
+ T_CLASS => true,
+ T_EXTENDS => true,
+ T_IMPLEMENTS => true,
+ T_NEW => true,
+ T_CONST => true,
+ T_NS_SEPARATOR => true,
+ T_USE => true,
+ T_NAMESPACE => true,
+ T_PAAMAYIM_NEKUDOTAYIM => true,
+ );
+ if (isset($context[$finalTokens[$lastNotEmptyToken]['code']]) === true) {
+ // Special case for syntax like: return new self
+ // where self should not be a string.
+ if ($finalTokens[$lastNotEmptyToken]['code'] === T_NEW
+ && strtolower($token[1]) === 'self'
+ ) {
+ $finalTokens[$newStackPtr] = array(
+ 'content' => $token[1],
+ 'code' => T_SELF,
+ 'type' => 'T_SELF',
+ );
+ } else {
+ $finalTokens[$newStackPtr] = array(
+ 'content' => $token[1],
+ 'code' => T_STRING,
+ 'type' => 'T_STRING',
+ );
+ }
+
+ $newStackPtr++;
+ continue;
+ }//end if
+ }//end if
+
+ $newToken = null;
+ if ($tokenIsArray === false) {
+ if (isset(self::$resolveTokenCache[$token[0]]) === true) {
+ $newToken = self::$resolveTokenCache[$token[0]];
+ }
+ } else {
+ $cacheKey = null;
+ if ($token[0] === T_STRING) {
+ $cacheKey = strtolower($token[1]);
+ } else if ($token[0] !== T_CURLY_OPEN) {
+ $cacheKey = $token[0];
+ }
+
+ if ($cacheKey !== null && isset(self::$resolveTokenCache[$cacheKey]) === true) {
+ $newToken = self::$resolveTokenCache[$cacheKey];
+ $newToken['content'] = $token[1];
+ }
+ }
+
+ if ($newToken === null) {
+ $newToken = self::standardiseToken($token);
+ }
+
+ // Convert colons that are actually the ELSE component of an
+ // inline IF statement.
+ if (empty($insideInlineIf) === false && $newToken['code'] === T_COLON) {
+ array_pop($insideInlineIf);
+ $newToken['code'] = T_INLINE_ELSE;
+ $newToken['type'] = 'T_INLINE_ELSE';
+ }
+
+ // This is a special condition for T_ARRAY tokens used for
+ // type hinting function arguments as being arrays. We want to keep
+ // the parenthesis map clean, so let's tag these tokens as
+ // T_ARRAY_HINT.
+ if ($newToken['code'] === T_ARRAY) {
+ for ($i = $stackPtr; $i < $numTokens; $i++) {
+ if ($tokens[$i] === '(') {
+ break;
+ } else if ($tokens[$i][0] === T_VARIABLE) {
+ $newToken['code'] = T_ARRAY_HINT;
+ $newToken['type'] = 'T_ARRAY_HINT';
+ break;
+ }
+ }
+ }
+
+ // This is a special case when checking PHP 5.5+ code in PHP < 5.5
+ // where "finally" should be T_FINALLY instead of T_STRING.
+ if ($newToken['code'] === T_STRING
+ && strtolower($newToken['content']) === 'finally'
+ ) {
+ $newToken['code'] = T_FINALLY;
+ $newToken['type'] = 'T_FINALLY';
+ }
+
+ // This is a special case for the PHP 5.5 classname::class syntax
+ // where "class" should be T_STRING instead of T_CLASS.
+ if (($newToken['code'] === T_CLASS
+ || $newToken['code'] === T_FUNCTION)
+ && $finalTokens[($newStackPtr - 1)]['code'] === T_DOUBLE_COLON
+ ) {
+ $newToken['code'] = T_STRING;
+ $newToken['type'] = 'T_STRING';
+ }
+
+ // This is a special case for PHP 5.6 use function and use const
+ // where "function" and "const" should be T_STRING instead of T_FUNCTION
+ // and T_CONST.
+ if (($newToken['code'] === T_FUNCTION
+ || $newToken['code'] === T_CONST)
+ && $finalTokens[$lastNotEmptyToken]['code'] === T_USE
+ ) {
+ $newToken['code'] = T_STRING;
+ $newToken['type'] = 'T_STRING';
+ }
+
+ // This is a special case for use groups in PHP 7+ where leaving
+ // the curly braces as their normal tokens would confuse
+ // the scope map and sniffs.
+ if ($newToken['code'] === T_OPEN_CURLY_BRACKET
+ && $finalTokens[$lastNotEmptyToken]['code'] === T_NS_SEPARATOR
+ ) {
+ $newToken['code'] = T_OPEN_USE_GROUP;
+ $newToken['type'] = 'T_OPEN_USE_GROUP';
+ $insideUseGroup = true;
+ }
+
+ if ($insideUseGroup === true && $newToken['code'] === T_CLOSE_CURLY_BRACKET) {
+ $newToken['code'] = T_CLOSE_USE_GROUP;
+ $newToken['type'] = 'T_CLOSE_USE_GROUP';
+ $insideUseGroup = false;
+ }
+
+ $finalTokens[$newStackPtr] = $newToken;
+ $newStackPtr++;
+ }//end if
+ }//end for
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** END PHP TOKENIZING ***".PHP_EOL;
+ }
+
+ return $finalTokens;
+
+ }//end tokenize()
+
+
+ /**
+ * Performs additional processing after main tokenizing.
+ *
+ * This additional processing checks for CASE statements that are using curly
+ * braces for scope openers and closers. It also turns some T_FUNCTION tokens
+ * into T_CLOSURE when they are not standard function definitions. It also
+ * detects short array syntax and converts those square brackets into new tokens.
+ * It also corrects some usage of the static and class keywords. It also
+ * assigns tokens to function return types.
+ *
+ * @return void
+ */
+ protected function processAdditional()
+ {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** START ADDITIONAL PHP PROCESSING ***".PHP_EOL;
+ }
+
+ $numTokens = count($this->tokens);
+ for ($i = ($numTokens - 1); $i >= 0; $i--) {
+ // Check for any unset scope conditions due to alternate IF/ENDIF syntax.
+ if (isset($this->tokens[$i]['scope_opener']) === true
+ && isset($this->tokens[$i]['scope_condition']) === false
+ ) {
+ $this->tokens[$i]['scope_condition'] = $this->tokens[$this->tokens[$i]['scope_opener']]['scope_condition'];
+ }
+
+ if ($this->tokens[$i]['code'] === T_FUNCTION) {
+ /*
+ Detect functions that are actually closures and
+ assign them a different token.
+ */
+
+ if (isset($this->tokens[$i]['scope_opener']) === true) {
+ for ($x = ($i + 1); $x < $numTokens; $x++) {
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false
+ && $this->tokens[$x]['code'] !== T_BITWISE_AND
+ ) {
+ break;
+ }
+ }
+
+ if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS) {
+ $this->tokens[$i]['code'] = T_CLOSURE;
+ $this->tokens[$i]['type'] = 'T_CLOSURE';
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $line = $this->tokens[$i]['line'];
+ echo "\t* token $i on line $line changed from T_FUNCTION to T_CLOSURE".PHP_EOL;
+ }
+
+ for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) {
+ if (isset($this->tokens[$x]['conditions'][$i]) === false) {
+ continue;
+ }
+
+ $this->tokens[$x]['conditions'][$i] = T_CLOSURE;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$x]['type'];
+ echo "\t\t* cleaned $x ($type) *".PHP_EOL;
+ }
+ }
+ }
+
+ $tokenAfterReturnTypeHint = $this->tokens[$i]['scope_opener'];
+ } else if (isset($this->tokens[$i]['parenthesis_closer']) === true) {
+ $tokenAfterReturnTypeHint = null;
+ for ($x = ($this->tokens[$i]['parenthesis_closer'] + 1); $x < $numTokens; $x++) {
+ if ($this->tokens[$x]['code'] === T_SEMICOLON) {
+ $tokenAfterReturnTypeHint = $x;
+ break;
+ }
+ }
+
+ if ($tokenAfterReturnTypeHint === null) {
+ // Probably a syntax error.
+ continue;
+ }
+ } else {
+ // Probably a syntax error.
+ continue;
+ }//end if
+
+ /*
+ Detect function return values and assign them
+ a special token, because PHP doesn't.
+ */
+
+ for ($x = ($tokenAfterReturnTypeHint - 1); $x > $i; $x--) {
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
+ if (in_array($this->tokens[$x]['code'], array(T_STRING, T_ARRAY, T_ARRAY_HINT, T_CALLABLE, T_SELF, T_PARENT), true) === true) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $line = $this->tokens[$x]['line'];
+ $type = $this->tokens[$x]['type'];
+ echo "\t* token $x on line $line changed from $type to T_RETURN_TYPE".PHP_EOL;
+ }
+
+ $this->tokens[$x]['code'] = T_RETURN_TYPE;
+ $this->tokens[$x]['type'] = 'T_RETURN_TYPE';
+
+ if (array_key_exists('parenthesis_opener', $this->tokens[$x]) === true) {
+ unset($this->tokens[$x]['parenthesis_opener']);
+ }
+
+ if (array_key_exists('parenthesis_closer', $this->tokens[$x]) === true) {
+ unset($this->tokens[$x]['parenthesis_closer']);
+ }
+
+ if (array_key_exists('parenthesis_owner', $this->tokens[$x]) === true) {
+ unset($this->tokens[$x]['parenthesis_owner']);
+ }
+ }//end if
+
+ break;
+ }//end if
+ }//end for
+
+ continue;
+ } else if ($this->tokens[$i]['code'] === T_CLASS && isset($this->tokens[$i]['scope_opener']) === true) {
+ /*
+ Detect anonymous classes and assign them a different token.
+ */
+
+ for ($x = ($i + 1); $x < $numTokens; $x++) {
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
+ break;
+ }
+ }
+
+ if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS
+ || $this->tokens[$x]['code'] === T_OPEN_CURLY_BRACKET
+ || $this->tokens[$x]['code'] === T_EXTENDS
+ || $this->tokens[$x]['code'] === T_IMPLEMENTS
+ ) {
+ $this->tokens[$i]['code'] = T_ANON_CLASS;
+ $this->tokens[$i]['type'] = 'T_ANON_CLASS';
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $line = $this->tokens[$i]['line'];
+ echo "\t* token $i on line $line changed from T_CLASS to T_ANON_CLASS".PHP_EOL;
+ }
+
+ for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) {
+ if (isset($this->tokens[$x]['conditions'][$i]) === false) {
+ continue;
+ }
+
+ $this->tokens[$x]['conditions'][$i] = T_ANON_CLASS;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$x]['type'];
+ echo "\t\t* cleaned $x ($type) *".PHP_EOL;
+ }
+ }
+ }
+
+ continue;
+ } else if ($this->tokens[$i]['code'] === T_OPEN_SQUARE_BRACKET) {
+ if (isset($this->tokens[$i]['bracket_closer']) === false) {
+ continue;
+ }
+
+ // Unless there is a variable or a bracket before this token,
+ // it is the start of an array being defined using the short syntax.
+ $isShortArray = false;
+ $allowed = array(
+ T_CLOSE_SQUARE_BRACKET => T_CLOSE_SQUARE_BRACKET,
+ T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
+ T_CLOSE_PARENTHESIS => T_CLOSE_PARENTHESIS,
+ T_VARIABLE => T_VARIABLE,
+ T_OBJECT_OPERATOR => T_OBJECT_OPERATOR,
+ T_STRING => T_STRING,
+ T_CONSTANT_ENCAPSED_STRING => T_CONSTANT_ENCAPSED_STRING,
+ );
+
+ for ($x = ($i - 1); $x > 0; $x--) {
+ // If we hit a scope opener, the statement has ended
+ // without finding anything, so it's probably an array
+ // using PHP 7.1 short list syntax.
+ if (isset($this->tokens[$x]['scope_opener']) === true) {
+ $isShortArray = true;
+ break;
+ }
+
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
+ if (isset($allowed[$this->tokens[$x]['code']]) === false) {
+ $isShortArray = true;
+ }
+
+ break;
+ }
+ }
+
+ if ($isShortArray === true) {
+ $this->tokens[$i]['code'] = T_OPEN_SHORT_ARRAY;
+ $this->tokens[$i]['type'] = 'T_OPEN_SHORT_ARRAY';
+
+ $closer = $this->tokens[$i]['bracket_closer'];
+ $this->tokens[$closer]['code'] = T_CLOSE_SHORT_ARRAY;
+ $this->tokens[$closer]['type'] = 'T_CLOSE_SHORT_ARRAY';
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $line = $this->tokens[$i]['line'];
+ echo "\t* token $i on line $line changed from T_OPEN_SQUARE_BRACKET to T_OPEN_SHORT_ARRAY".PHP_EOL;
+ $line = $this->tokens[$closer]['line'];
+ echo "\t* token $closer on line $line changed from T_CLOSE_SQUARE_BRACKET to T_CLOSE_SHORT_ARRAY".PHP_EOL;
+ }
+ }
+
+ continue;
+ } else if ($this->tokens[$i]['code'] === T_STATIC) {
+ for ($x = ($i - 1); $x > 0; $x--) {
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
+ break;
+ }
+ }
+
+ if ($this->tokens[$x]['code'] === T_INSTANCEOF) {
+ $this->tokens[$i]['code'] = T_STRING;
+ $this->tokens[$i]['type'] = 'T_STRING';
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $line = $this->tokens[$i]['line'];
+ echo "\t* token $i on line $line changed from T_STATIC to T_STRING".PHP_EOL;
+ }
+ }
+
+ continue;
+ } else if ($this->tokens[$i]['code'] === T_ECHO && $this->tokens[$i]['content'] === '<?=') {
+ // HHVM tokenizes <?= as T_ECHO but it should be T_OPEN_TAG_WITH_ECHO.
+ $this->tokens[$i]['code'] = T_OPEN_TAG_WITH_ECHO;
+ $this->tokens[$i]['type'] = 'T_OPEN_TAG_WITH_ECHO';
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $line = $this->tokens[$i]['line'];
+ echo "\t* token $i on line $line changed from T_ECHO to T_OPEN_TAG_WITH_ECHO".PHP_EOL;
+ }
+ } else if ($this->tokens[$i]['code'] === T_TRUE
+ || $this->tokens[$i]['code'] === T_FALSE
+ || $this->tokens[$i]['code'] === T_NULL
+ ) {
+ for ($x = ($i + 1); $i < $numTokens; $x++) {
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
+ // Non-whitespace content.
+ break;
+ }
+ }
+
+ $context = array(
+ T_OBJECT_OPERATOR => true,
+ T_NS_SEPARATOR => true,
+ T_PAAMAYIM_NEKUDOTAYIM => true,
+ );
+ if (isset($context[$this->tokens[$x]['code']]) === true) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $line = $this->tokens[$i]['line'];
+ $type = $this->tokens[$i]['type'];
+ echo "\t* token $i on line $line changed from $type to T_STRING".PHP_EOL;
+ }
+
+ $this->tokens[$i]['code'] = T_STRING;
+ $this->tokens[$i]['type'] = 'T_STRING';
+ }
+ } else if ($this->tokens[$i]['code'] === T_CONST) {
+ // Context sensitive keywords support.
+ for ($x = ($i + 1); $i < $numTokens; $x++) {
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
+ // Non-whitespace content.
+ break;
+ }
+ }
+
+ if ($this->tokens[$x]['code'] !== T_STRING) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $line = $this->tokens[$x]['line'];
+ $type = $this->tokens[$x]['type'];
+ echo "\t* token $x on line $line changed from $type to T_STRING".PHP_EOL;
+ }
+
+ $this->tokens[$x]['code'] = T_STRING;
+ $this->tokens[$x]['type'] = 'T_STRING';
+ }
+ }//end if
+
+ if (($this->tokens[$i]['code'] !== T_CASE
+ && $this->tokens[$i]['code'] !== T_DEFAULT)
+ || isset($this->tokens[$i]['scope_opener']) === false
+ ) {
+ // Only interested in CASE and DEFAULT statements from here on in.
+ continue;
+ }
+
+ $scopeOpener = $this->tokens[$i]['scope_opener'];
+ $scopeCloser = $this->tokens[$i]['scope_closer'];
+
+ // If the first char after the opener is a curly brace
+ // and that brace has been ignored, it is actually
+ // opening this case statement and the opener and closer are
+ // probably set incorrectly.
+ for ($x = ($scopeOpener + 1); $x < $numTokens; $x++) {
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
+ // Non-whitespace content.
+ break;
+ }
+ }
+
+ if ($this->tokens[$x]['code'] === T_CASE || $this->tokens[$x]['code'] === T_DEFAULT) {
+ // Special case for multiple CASE statements that share the same
+ // closer. Because we are going backwards through the file, this next
+ // CASE statement is already fixed, so just use its closer and don't
+ // worry about fixing anything.
+ $newCloser = $this->tokens[$x]['scope_closer'];
+ $this->tokens[$i]['scope_closer'] = $newCloser;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $oldType = $this->tokens[$scopeCloser]['type'];
+ $newType = $this->tokens[$newCloser]['type'];
+ $line = $this->tokens[$i]['line'];
+ echo "\t* token $i (T_CASE) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
+ }
+
+ continue;
+ }
+
+ if ($this->tokens[$x]['code'] !== T_OPEN_CURLY_BRACKET
+ || isset($this->tokens[$x]['scope_condition']) === true
+ ) {
+ // Not a CASE/DEFAULT with a curly brace opener.
+ continue;
+ }
+
+ // The closer for this CASE/DEFAULT should be the closing curly brace and
+ // not whatever it already is. The opener needs to be the opening curly
+ // brace so everything matches up.
+ $newCloser = $this->tokens[$x]['bracket_closer'];
+ foreach (array($i, $x, $newCloser) as $index) {
+ $this->tokens[$index]['scope_condition'] = $i;
+ $this->tokens[$index]['scope_opener'] = $x;
+ $this->tokens[$index]['scope_closer'] = $newCloser;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $line = $this->tokens[$i]['line'];
+ $tokenType = $this->tokens[$i]['type'];
+
+ $oldType = $this->tokens[$scopeOpener]['type'];
+ $newType = $this->tokens[$x]['type'];
+ echo "\t* token $i ($tokenType) on line $line opener changed from $scopeOpener ($oldType) to $x ($newType)".PHP_EOL;
+
+ $oldType = $this->tokens[$scopeCloser]['type'];
+ $newType = $this->tokens[$newCloser]['type'];
+ echo "\t* token $i ($tokenType) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
+ }
+
+ if ($this->tokens[$scopeOpener]['scope_condition'] === $i) {
+ unset($this->tokens[$scopeOpener]['scope_condition']);
+ unset($this->tokens[$scopeOpener]['scope_opener']);
+ unset($this->tokens[$scopeOpener]['scope_closer']);
+ }
+
+ if ($this->tokens[$scopeCloser]['scope_condition'] === $i) {
+ unset($this->tokens[$scopeCloser]['scope_condition']);
+ unset($this->tokens[$scopeCloser]['scope_opener']);
+ unset($this->tokens[$scopeCloser]['scope_closer']);
+ } else {
+ // We were using a shared closer. All tokens that were
+ // sharing this closer with us, except for the scope condition
+ // and it's opener, need to now point to the new closer.
+ $condition = $this->tokens[$scopeCloser]['scope_condition'];
+ $start = ($this->tokens[$condition]['scope_opener'] + 1);
+ for ($y = $start; $y < $scopeCloser; $y++) {
+ if (isset($this->tokens[$y]['scope_closer']) === true
+ && $this->tokens[$y]['scope_closer'] === $scopeCloser
+ ) {
+ $this->tokens[$y]['scope_closer'] = $newCloser;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $line = $this->tokens[$y]['line'];
+ $tokenType = $this->tokens[$y]['type'];
+ $oldType = $this->tokens[$scopeCloser]['type'];
+ $newType = $this->tokens[$newCloser]['type'];
+ echo "\t\t* token $y ($tokenType) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
+ }
+ }
+ }
+ }//end if
+
+ unset($this->tokens[$x]['bracket_opener']);
+ unset($this->tokens[$x]['bracket_closer']);
+ unset($this->tokens[$newCloser]['bracket_opener']);
+ unset($this->tokens[$newCloser]['bracket_closer']);
+ $this->tokens[$scopeCloser]['conditions'][] = $i;
+
+ // Now fix up all the tokens that think they are
+ // inside the CASE/DEFAULT statement when they are really outside.
+ for ($x = $newCloser; $x < $scopeCloser; $x++) {
+ foreach ($this->tokens[$x]['conditions'] as $num => $oldCond) {
+ if ($oldCond === $this->tokens[$i]['code']) {
+ $oldConditions = $this->tokens[$x]['conditions'];
+ unset($this->tokens[$x]['conditions'][$num]);
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$x]['type'];
+ $oldConds = '';
+ foreach ($oldConditions as $condition) {
+ $oldConds .= Util\Tokens::tokenName($condition).',';
+ }
+
+ $oldConds = rtrim($oldConds, ',');
+
+ $newConds = '';
+ foreach ($this->tokens[$x]['conditions'] as $condition) {
+ $newConds .= Util\Tokens::tokenName($condition).',';
+ }
+
+ $newConds = rtrim($newConds, ',');
+
+ echo "\t\t* cleaned $x ($type) *".PHP_EOL;
+ echo "\t\t\t=> conditions changed from $oldConds to $newConds".PHP_EOL;
+ }
+
+ break;
+ }//end if
+ }//end foreach
+ }//end for
+ }//end for
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** END ADDITIONAL PHP PROCESSING ***".PHP_EOL;
+ }
+
+ }//end processAdditional()
+
+
+ /**
+ * Takes a token produced from <code>token_get_all()</code> and produces a
+ * more uniform token.
+ *
+ * @param string|array $token The token to convert.
+ *
+ * @return array The new token.
+ */
+ public static function standardiseToken($token)
+ {
+ if (isset($token[1]) === false) {
+ if (isset(self::$resolveTokenCache[$token[0]]) === true) {
+ return self::$resolveTokenCache[$token[0]];
+ }
+ } else {
+ $cacheKey = null;
+ if ($token[0] === T_STRING) {
+ $cacheKey = strtolower($token[1]);
+ } else if ($token[0] !== T_CURLY_OPEN) {
+ $cacheKey = $token[0];
+ }
+
+ if ($cacheKey !== null && isset(self::$resolveTokenCache[$cacheKey]) === true) {
+ $newToken = self::$resolveTokenCache[$cacheKey];
+ $newToken['content'] = $token[1];
+ return $newToken;
+ }
+ }
+
+ if (isset($token[1]) === false) {
+ return self::resolveSimpleToken($token[0]);
+ }
+
+ if ($token[0] === T_STRING) {
+ switch ($cacheKey) {
+ case 'false':
+ $newToken['type'] = 'T_FALSE';
+ break;
+ case 'true':
+ $newToken['type'] = 'T_TRUE';
+ break;
+ case 'null':
+ $newToken['type'] = 'T_NULL';
+ break;
+ case 'self':
+ $newToken['type'] = 'T_SELF';
+ break;
+ case 'parent':
+ $newToken['type'] = 'T_PARENT';
+ break;
+ default:
+ $newToken['type'] = 'T_STRING';
+ break;
+ }
+
+ $newToken['code'] = constant($newToken['type']);
+
+ self::$resolveTokenCache[$cacheKey] = $newToken;
+ } else if ($token[0] === T_CURLY_OPEN) {
+ $newToken = array(
+ 'code' => T_OPEN_CURLY_BRACKET,
+ 'type' => 'T_OPEN_CURLY_BRACKET',
+ );
+ } else {
+ $newToken = array(
+ 'code' => $token[0],
+ 'type' => token_name($token[0]),
+ );
+
+ self::$resolveTokenCache[$token[0]] = $newToken;
+ }//end if
+
+ $newToken['content'] = $token[1];
+ return $newToken;
+
+ }//end standardiseToken()
+
+
+ /**
+ * Converts simple tokens into a format that conforms to complex tokens
+ * produced by token_get_all().
+ *
+ * Simple tokens are tokens that are not in array form when produced from
+ * token_get_all().
+ *
+ * @param string $token The simple token to convert.
+ *
+ * @return array The new token in array format.
+ */
+ public static function resolveSimpleToken($token)
+ {
+ $newToken = array();
+
+ switch ($token) {
+ case '{':
+ $newToken['type'] = 'T_OPEN_CURLY_BRACKET';
+ break;
+ case '}':
+ $newToken['type'] = 'T_CLOSE_CURLY_BRACKET';
+ break;
+ case '[':
+ $newToken['type'] = 'T_OPEN_SQUARE_BRACKET';
+ break;
+ case ']':
+ $newToken['type'] = 'T_CLOSE_SQUARE_BRACKET';
+ break;
+ case '(':
+ $newToken['type'] = 'T_OPEN_PARENTHESIS';
+ break;
+ case ')':
+ $newToken['type'] = 'T_CLOSE_PARENTHESIS';
+ break;
+ case ':':
+ $newToken['type'] = 'T_COLON';
+ break;
+ case '.':
+ $newToken['type'] = 'T_STRING_CONCAT';
+ break;
+ case ';':
+ $newToken['type'] = 'T_SEMICOLON';
+ break;
+ case '=':
+ $newToken['type'] = 'T_EQUAL';
+ break;
+ case '*':
+ $newToken['type'] = 'T_MULTIPLY';
+ break;
+ case '/':
+ $newToken['type'] = 'T_DIVIDE';
+ break;
+ case '+':
+ $newToken['type'] = 'T_PLUS';
+ break;
+ case '-':
+ $newToken['type'] = 'T_MINUS';
+ break;
+ case '%':
+ $newToken['type'] = 'T_MODULUS';
+ break;
+ case '^':
+ $newToken['type'] = 'T_BITWISE_XOR';
+ break;
+ case '&':
+ $newToken['type'] = 'T_BITWISE_AND';
+ break;
+ case '|':
+ $newToken['type'] = 'T_BITWISE_OR';
+ break;
+ case '<':
+ $newToken['type'] = 'T_LESS_THAN';
+ break;
+ case '>':
+ $newToken['type'] = 'T_GREATER_THAN';
+ break;
+ case '!':
+ $newToken['type'] = 'T_BOOLEAN_NOT';
+ break;
+ case ',':
+ $newToken['type'] = 'T_COMMA';
+ break;
+ case '@':
+ $newToken['type'] = 'T_ASPERAND';
+ break;
+ case '$':
+ $newToken['type'] = 'T_DOLLAR';
+ break;
+ case '`':
+ $newToken['type'] = 'T_BACKTICK';
+ break;
+ default:
+ $newToken['type'] = 'T_NONE';
+ break;
+ }//end switch
+
+ $newToken['code'] = constant($newToken['type']);
+ $newToken['content'] = $token;
+
+ self::$resolveTokenCache[$token] = $newToken;
+ return $newToken;
+
+ }//end resolveSimpleToken()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * The base tokenizer class.
+ *
+ * @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\Tokenizers;
+
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Util;
+
+abstract class Tokenizer
+{
+
+ /**
+ * The config data for the run.
+ *
+ * @var \PHP_CodeSniffer\Config
+ */
+ protected $config = null;
+
+ /**
+ * The EOL char used in the content.
+ *
+ * @var string
+ */
+ protected $eolChar = array();
+
+ /**
+ * A token-based representation of the content.
+ *
+ * @var array
+ */
+ protected $tokens = array();
+
+ /**
+ * Known lengths of tokens.
+ *
+ * @var array<int, int>
+ */
+ public $knownLengths = array();
+
+ /**
+ * A list of lines being ignored due to error suppression comments.
+ *
+ * @var array
+ */
+ public $ignoredLines = array();
+
+
+ /**
+ * Initialise and run the tokenizer.
+ *
+ * @param string $content The content to tokenize,
+ * @param \PHP_CodeSniffer\Config | null $config The config data for the run.
+ * @param string $eolChar The EOL char used in the content.
+ *
+ * @return void
+ * @throws TokenizerException If the file appears to be minified.
+ */
+ public function __construct($content, $config, $eolChar='\n')
+ {
+ $this->eolChar = $eolChar;
+
+ $this->config = $config;
+ $this->tokens = $this->tokenize($content);
+
+ if ($config === null) {
+ return;
+ }
+
+ $this->createPositionMap();
+ $this->createTokenMap();
+ $this->createParenthesisNestingMap();
+ $this->createScopeMap();
+ $this->createLevelMap();
+
+ // Allow the tokenizer to do additional processing if required.
+ $this->processAdditional();
+
+ }//end __construct()
+
+
+ /**
+ * Checks the content to see if it looks minified.
+ *
+ * @param string $content The content to tokenize,
+ * @param string $eolChar The EOL char used in the content.
+ *
+ * @return boolean
+ */
+ protected function isMinifiedContent($content, $eolChar='\n')
+ {
+ // Minified files often have a very large number of characters per line
+ // and cause issues when tokenizing.
+ $numChars = strlen($content);
+ $numLines = (substr_count($content, $eolChar) + 1);
+ $average = ($numChars / $numLines);
+ if ($average > 100) {
+ return true;
+ }
+
+ return false;
+
+ }//end isMinifiedContent()
+
+
+ /**
+ * Gets the array of tokens.
+ *
+ * @return array
+ */
+ public function getTokens()
+ {
+ return $this->tokens;
+
+ }//end getTokens()
+
+
+ /**
+ * Creates an array of tokens when given some content.
+ *
+ * @param string $string The string to tokenize.
+ *
+ * @return array
+ */
+ abstract protected function tokenize($string);
+
+
+ /**
+ * Performs additional processing after main tokenizing.
+ *
+ * @return void
+ */
+ abstract protected function processAdditional();
+
+
+ /**
+ * Sets token position information.
+ *
+ * Can also convert tabs into spaces. Each tab can represent between
+ * 1 and $width spaces, so this cannot be a straight string replace.
+ *
+ * @return void
+ */
+ private function createPositionMap()
+ {
+ $currColumn = 1;
+ $lineNumber = 1;
+ $eolLen = (strlen($this->eolChar) * -1);
+ $ignoring = false;
+ $inTests = defined('PHP_CODESNIFFER_IN_TESTS');
+
+ $checkEncoding = false;
+ if (function_exists('iconv_strlen') === true) {
+ $checkEncoding = true;
+ }
+
+ $checkAnnotations = $this->config->annotations;
+
+ $this->tokensWithTabs = array(
+ T_WHITESPACE => true,
+ T_COMMENT => true,
+ T_DOC_COMMENT => true,
+ T_DOC_COMMENT_WHITESPACE => true,
+ T_DOC_COMMENT_STRING => true,
+ T_CONSTANT_ENCAPSED_STRING => true,
+ T_DOUBLE_QUOTED_STRING => true,
+ T_HEREDOC => true,
+ T_NOWDOC => true,
+ T_INLINE_HTML => true,
+ );
+
+ $this->numTokens = count($this->tokens);
+ for ($i = 0; $i < $this->numTokens; $i++) {
+ $this->tokens[$i]['line'] = $lineNumber;
+ $this->tokens[$i]['column'] = $currColumn;
+
+ if (isset($this->knownLengths[$this->tokens[$i]['code']]) === true) {
+ // There are no tabs in the tokens we know the length of.
+ $length = $this->knownLengths[$this->tokens[$i]['code']];
+ $currColumn += $length;
+ } else if ($this->config->tabWidth === 0
+ || isset($this->tokensWithTabs[$this->tokens[$i]['code']]) === false
+ || strpos($this->tokens[$i]['content'], "\t") === false
+ ) {
+ // There are no tabs in this content, or we aren't replacing them.
+ if ($checkEncoding === true) {
+ // Not using the default encoding, so take a bit more care.
+ $length = @iconv_strlen($this->tokens[$i]['content'], $this->config->encoding);
+ if ($length === false) {
+ // String contained invalid characters, so revert to default.
+ $length = strlen($this->tokens[$i]['content']);
+ }
+ } else {
+ $length = strlen($this->tokens[$i]['content']);
+ }
+
+ $currColumn += $length;
+ } else {
+ $this->replaceTabsInToken($this->tokens[$i]);
+ $length = $this->tokens[$i]['length'];
+ $currColumn += $length;
+ }//end if
+
+ $this->tokens[$i]['length'] = $length;
+
+ if (isset($this->knownLengths[$this->tokens[$i]['code']]) === false
+ && strpos($this->tokens[$i]['content'], $this->eolChar) !== false
+ ) {
+ $lineNumber++;
+ $currColumn = 1;
+
+ // Newline chars are not counted in the token length.
+ $this->tokens[$i]['length'] += $eolLen;
+ }
+
+ if ($checkAnnotations === true
+ && ($this->tokens[$i]['code'] === T_COMMENT
+ || $this->tokens[$i]['code'] === T_DOC_COMMENT_TAG
+ || ($inTests === true && $this->tokens[$i]['code'] === T_INLINE_HTML))
+ ) {
+ if (strpos($this->tokens[$i]['content'], '@codingStandards') !== false) {
+ if ($ignoring === false
+ && strpos($this->tokens[$i]['content'], '@codingStandardsIgnoreStart') !== false
+ ) {
+ $ignoring = true;
+ } else if ($ignoring === true
+ && strpos($this->tokens[$i]['content'], '@codingStandardsIgnoreEnd') !== false
+ ) {
+ $ignoring = false;
+ // Ignore this comment too.
+ $this->ignoredLines[$this->tokens[$i]['line']] = true;
+ } else if ($ignoring === false
+ && strpos($this->tokens[$i]['content'], '@codingStandardsIgnoreLine') !== false
+ ) {
+ $this->ignoredLines[($this->tokens[$i]['line'] + 1)] = true;
+ // Ignore this comment too.
+ $this->ignoredLines[$this->tokens[$i]['line']] = true;
+ }
+ }
+ }//end if
+
+ if ($ignoring === true) {
+ $this->ignoredLines[$this->tokens[$i]['line']] = true;
+ }
+ }//end for
+
+ }//end createPositionMap()
+
+
+ /**
+ * Replaces tabs in original token content with spaces.
+ *
+ * Each tab can represent between 1 and $config->tabWidth spaces,
+ * so this cannot be a straight string replace. The original content
+ * is placed into an orig_content index and the new token length is also
+ * set in the length index.
+ *
+ * @param array $token The token to replace tabs inside.
+ * @param string $prefix The character to use to represent the start of a tab.
+ * @param string $padding The character to use to represent the end of a tab.
+ *
+ * @return void
+ */
+ public function replaceTabsInToken(&$token, $prefix=' ', $padding=' ')
+ {
+ $checkEncoding = false;
+ if (function_exists('iconv_strlen') === true) {
+ $checkEncoding = true;
+ }
+
+ $currColumn = $token['column'];
+ $tabWidth = $this->config->tabWidth;
+ if ($tabWidth === 0) {
+ $tabWidth = 1;
+ }
+
+ if (str_replace("\t", '', $token['content']) === '') {
+ // String only contains tabs, so we can shortcut the process.
+ $numTabs = strlen($token['content']);
+
+ $newContent = '';
+ $firstTabSize = ($tabWidth - (($currColumn - 1) % $tabWidth));
+ $length = ($firstTabSize + ($tabWidth * ($numTabs - 1)));
+ $currColumn += $length;
+ $newContent = $prefix.str_repeat($padding, ($length - 1));
+ } else {
+ // We need to determine the length of each tab.
+ $tabs = explode("\t", $token['content']);
+
+ $numTabs = (count($tabs) - 1);
+ $tabNum = 0;
+ $newContent = '';
+ $length = 0;
+
+ foreach ($tabs as $content) {
+ if ($content !== '') {
+ $newContent .= $content;
+ if ($checkEncoding === true) {
+ // Not using the default encoding, so take a bit more care.
+ $contentLength = @iconv_strlen($content, $this->config->encoding);
+ if ($contentLength === false) {
+ // String contained invalid characters, so revert to default.
+ $contentLength = strlen($content);
+ }
+ } else {
+ $contentLength = strlen($content);
+ }
+
+ $currColumn += $contentLength;
+ $length += $contentLength;
+ }
+
+ // The last piece of content does not have a tab after it.
+ if ($tabNum === $numTabs) {
+ break;
+ }
+
+ // Process the tab that comes after the content.
+ $lastCurrColumn = $currColumn;
+ $tabNum++;
+
+ // Move the pointer to the next tab stop.
+ if (($currColumn % $tabWidth) === 0) {
+ // This is the first tab, and we are already at a
+ // tab stop, so this tab counts as a single space.
+ $currColumn++;
+ } else {
+ $currColumn++;
+ while (($currColumn % $tabWidth) !== 0) {
+ $currColumn++;
+ }
+
+ $currColumn++;
+ }
+
+ $length += ($currColumn - $lastCurrColumn);
+ $newContent .= $prefix.str_repeat($padding, ($currColumn - $lastCurrColumn - 1));
+ }//end foreach
+ }//end if
+
+ $token['orig_content'] = $token['content'];
+ $token['content'] = $newContent;
+ $token['length'] = $length;
+
+ }//end replaceTabsInToken()
+
+
+ /**
+ * Creates a map of brackets positions.
+ *
+ * @return void
+ */
+ private function createTokenMap()
+ {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** START TOKEN MAP ***".PHP_EOL;
+ }
+
+ $squareOpeners = array();
+ $curlyOpeners = array();
+ $this->numTokens = count($this->tokens);
+
+ $openers = array();
+ $openOwner = null;
+
+ for ($i = 0; $i < $this->numTokens; $i++) {
+ /*
+ Parenthesis mapping.
+ */
+
+ if (isset(Util\Tokens::$parenthesisOpeners[$this->tokens[$i]['code']]) === true) {
+ $this->tokens[$i]['parenthesis_opener'] = null;
+ $this->tokens[$i]['parenthesis_closer'] = null;
+ $this->tokens[$i]['parenthesis_owner'] = $i;
+ $openOwner = $i;
+ } else if ($this->tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
+ $openers[] = $i;
+ $this->tokens[$i]['parenthesis_opener'] = $i;
+ if ($openOwner !== null) {
+ $this->tokens[$openOwner]['parenthesis_opener'] = $i;
+ $this->tokens[$i]['parenthesis_owner'] = $openOwner;
+ $openOwner = null;
+ }
+ } else if ($this->tokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
+ // Did we set an owner for this set of parenthesis?
+ $numOpeners = count($openers);
+ if ($numOpeners !== 0) {
+ $opener = array_pop($openers);
+ if (isset($this->tokens[$opener]['parenthesis_owner']) === true) {
+ $owner = $this->tokens[$opener]['parenthesis_owner'];
+
+ $this->tokens[$owner]['parenthesis_closer'] = $i;
+ $this->tokens[$i]['parenthesis_owner'] = $owner;
+ }
+
+ $this->tokens[$i]['parenthesis_opener'] = $opener;
+ $this->tokens[$i]['parenthesis_closer'] = $i;
+ $this->tokens[$opener]['parenthesis_closer'] = $i;
+ }
+ }//end if
+
+ /*
+ Bracket mapping.
+ */
+
+ switch ($this->tokens[$i]['code']) {
+ case T_OPEN_SQUARE_BRACKET:
+ $squareOpeners[] = $i;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", count($squareOpeners));
+ echo str_repeat("\t", count($curlyOpeners));
+ echo "=> Found square bracket opener at $i".PHP_EOL;
+ }
+ break;
+ case T_OPEN_CURLY_BRACKET:
+ if (isset($this->tokens[$i]['scope_closer']) === false) {
+ $curlyOpeners[] = $i;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", count($squareOpeners));
+ echo str_repeat("\t", count($curlyOpeners));
+ echo "=> Found curly bracket opener at $i".PHP_EOL;
+ }
+ }
+ break;
+ case T_CLOSE_SQUARE_BRACKET:
+ if (empty($squareOpeners) === false) {
+ $opener = array_pop($squareOpeners);
+ $this->tokens[$i]['bracket_opener'] = $opener;
+ $this->tokens[$i]['bracket_closer'] = $i;
+ $this->tokens[$opener]['bracket_opener'] = $opener;
+ $this->tokens[$opener]['bracket_closer'] = $i;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", count($squareOpeners));
+ echo str_repeat("\t", count($curlyOpeners));
+ echo "\t=> Found square bracket closer at $i for $opener".PHP_EOL;
+ }
+ }
+ break;
+ case T_CLOSE_CURLY_BRACKET:
+ if (empty($curlyOpeners) === false
+ && isset($this->tokens[$i]['scope_opener']) === false
+ ) {
+ $opener = array_pop($curlyOpeners);
+ $this->tokens[$i]['bracket_opener'] = $opener;
+ $this->tokens[$i]['bracket_closer'] = $i;
+ $this->tokens[$opener]['bracket_opener'] = $opener;
+ $this->tokens[$opener]['bracket_closer'] = $i;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", count($squareOpeners));
+ echo str_repeat("\t", count($curlyOpeners));
+ echo "\t=> Found curly bracket closer at $i for $opener".PHP_EOL;
+ }
+ }
+ break;
+ default:
+ continue;
+ }//end switch
+ }//end for
+
+ // Cleanup for any openers that we didn't find closers for.
+ // This typically means there was a syntax error breaking things.
+ foreach ($openers as $opener) {
+ unset($this->tokens[$opener]['parenthesis_opener']);
+ unset($this->tokens[$opener]['parenthesis_owner']);
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** END TOKEN MAP ***".PHP_EOL;
+ }
+
+ }//end createTokenMap()
+
+
+ /**
+ * Creates a map for the parenthesis tokens that surround other tokens.
+ *
+ * @return void
+ */
+ private function createParenthesisNestingMap()
+ {
+ $map = array();
+ for ($i = 0; $i < $this->numTokens; $i++) {
+ if (isset($this->tokens[$i]['parenthesis_opener']) === true
+ && $i === $this->tokens[$i]['parenthesis_opener']
+ ) {
+ if (empty($map) === false) {
+ $this->tokens[$i]['nested_parenthesis'] = $map;
+ }
+
+ if (isset($this->tokens[$i]['parenthesis_closer']) === true) {
+ $map[$this->tokens[$i]['parenthesis_opener']]
+ = $this->tokens[$i]['parenthesis_closer'];
+ }
+ } else if (isset($this->tokens[$i]['parenthesis_closer']) === true
+ && $i === $this->tokens[$i]['parenthesis_closer']
+ ) {
+ array_pop($map);
+ if (empty($map) === false) {
+ $this->tokens[$i]['nested_parenthesis'] = $map;
+ }
+ } else {
+ if (empty($map) === false) {
+ $this->tokens[$i]['nested_parenthesis'] = $map;
+ }
+ }//end if
+ }//end for
+
+ }//end createParenthesisNestingMap()
+
+
+ /**
+ * Creates a scope map of tokens that open scopes.
+ *
+ * @return void
+ * @see recurseScopeMap()
+ */
+ private function createScopeMap()
+ {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** START SCOPE MAP ***".PHP_EOL;
+ }
+
+ for ($i = 0; $i < $this->numTokens; $i++) {
+ // Check to see if the current token starts a new scope.
+ if (isset($this->scopeOpeners[$this->tokens[$i]['code']]) === true) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$i]['type'];
+ $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
+ echo "\tStart scope map at $i:$type => $content".PHP_EOL;
+ }
+
+ if (isset($this->tokens[$i]['scope_condition']) === true) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t* already processed, skipping *".PHP_EOL;
+ }
+
+ continue;
+ }
+
+ $i = $this->recurseScopeMap($i);
+ }//end if
+ }//end for
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** END SCOPE MAP ***".PHP_EOL;
+ }
+
+ }//end createScopeMap()
+
+
+ /**
+ * Recurses though the scope openers to build a scope map.
+ *
+ * @param int $stackPtr The position in the stack of the token that
+ * opened the scope (eg. an IF token or FOR token).
+ * @param int $depth How many scope levels down we are.
+ * @param int $ignore How many curly braces we are ignoring.
+ *
+ * @return int The position in the stack that closed the scope.
+ */
+ private function recurseScopeMap($stackPtr, $depth=1, &$ignore=0)
+ {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "=> Begin scope map recursion at token $stackPtr with depth $depth".PHP_EOL;
+ }
+
+ $opener = null;
+ $currType = $this->tokens[$stackPtr]['code'];
+ $startLine = $this->tokens[$stackPtr]['line'];
+
+ // We will need this to restore the value if we end up
+ // returning a token ID that causes our calling function to go back
+ // over already ignored braces.
+ $originalIgnore = $ignore;
+
+ // If the start token for this scope opener is the same as
+ // the scope token, we have already found our opener.
+ if (isset($this->scopeOpeners[$currType]['start'][$currType]) === true) {
+ $opener = $stackPtr;
+ }
+
+ for ($i = ($stackPtr + 1); $i < $this->numTokens; $i++) {
+ $tokenType = $this->tokens[$i]['code'];
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$i]['type'];
+ $line = $this->tokens[$i]['line'];
+ $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
+
+ echo str_repeat("\t", $depth);
+ echo "Process token $i on line $line [";
+ if ($opener !== null) {
+ echo "opener:$opener;";
+ }
+
+ if ($ignore > 0) {
+ echo "ignore=$ignore;";
+ }
+
+ echo "]: $type => $content".PHP_EOL;
+ }//end if
+
+ // Very special case for IF statements in PHP that can be defined without
+ // scope tokens. E.g., if (1) 1; 1 ? (1 ? 1 : 1) : 1;
+ // If an IF statement below this one has an opener but no
+ // keyword, the opener will be incorrectly assigned to this IF statement.
+ // The same case also applies to USE statements, which don't have to have
+ // openers, so a following USE statement can cause an incorrect brace match.
+ if (($currType === T_IF || $currType === T_ELSE || $currType === T_USE)
+ && $opener === null
+ && ($this->tokens[$i]['code'] === T_SEMICOLON
+ || $this->tokens[$i]['code'] === T_CLOSE_TAG)
+ ) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$stackPtr]['type'];
+ echo str_repeat("\t", $depth);
+ if ($this->tokens[$i]['code'] === T_SEMICOLON) {
+ $closerType = 'semicolon';
+ } else {
+ $closerType = 'close tag';
+ }
+
+ echo "=> Found $closerType before scope opener for $stackPtr:$type, bailing".PHP_EOL;
+ }
+
+ return $i;
+ }
+
+ // Special case for PHP control structures that have no braces.
+ // If we find a curly brace closer before we find the opener,
+ // we're not going to find an opener. That closer probably belongs to
+ // a control structure higher up.
+ if ($opener === null
+ && $ignore === 0
+ && $tokenType === T_CLOSE_CURLY_BRACKET
+ && isset($this->scopeOpeners[$currType]['end'][$tokenType]) === true
+ ) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$stackPtr]['type'];
+ echo str_repeat("\t", $depth);
+ echo "=> Found curly brace closer before scope opener for $stackPtr:$type, bailing".PHP_EOL;
+ }
+
+ return ($i - 1);
+ }
+
+ if ($opener !== null
+ && (isset($this->tokens[$i]['scope_opener']) === false
+ || $this->scopeOpeners[$this->tokens[$stackPtr]['code']]['shared'] === true)
+ && isset($this->scopeOpeners[$currType]['end'][$tokenType]) === true
+ ) {
+ if ($ignore > 0 && $tokenType === T_CLOSE_CURLY_BRACKET) {
+ // The last opening bracket must have been for a string
+ // offset or alike, so let's ignore it.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo '* finished ignoring curly brace *'.PHP_EOL;
+ }
+
+ $ignore--;
+ continue;
+ } else if ($this->tokens[$opener]['code'] === T_OPEN_CURLY_BRACKET
+ && $tokenType !== T_CLOSE_CURLY_BRACKET
+ ) {
+ // The opener is a curly bracket so the closer must be a curly bracket as well.
+ // We ignore this closer to handle cases such as T_ELSE or T_ELSEIF being considered
+ // a closer of T_IF when it should not.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$stackPtr]['type'];
+ echo str_repeat("\t", $depth);
+ echo "=> Ignoring non-curly scope closer for $stackPtr:$type".PHP_EOL;
+ }
+ } else {
+ $scopeCloser = $i;
+ $todo = array(
+ $stackPtr,
+ $opener,
+ );
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$stackPtr]['type'];
+ $closerType = $this->tokens[$scopeCloser]['type'];
+ echo str_repeat("\t", $depth);
+ echo "=> Found scope closer ($scopeCloser:$closerType) for $stackPtr:$type".PHP_EOL;
+ }
+
+ $validCloser = true;
+ if (($this->tokens[$stackPtr]['code'] === T_IF || $this->tokens[$stackPtr]['code'] === T_ELSEIF)
+ && ($tokenType === T_ELSE || $tokenType === T_ELSEIF)
+ ) {
+ // To be a closer, this token must have an opener.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "* closer needs to be tested *".PHP_EOL;
+ }
+
+ $i = self::recurseScopeMap($i, ($depth + 1), $ignore);
+
+ if (isset($this->tokens[$scopeCloser]['scope_opener']) === false) {
+ $validCloser = false;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "* closer is not valid (no opener found) *".PHP_EOL;
+ }
+ } else if ($this->tokens[$this->tokens[$scopeCloser]['scope_opener']]['code'] !== $this->tokens[$opener]['code']) {
+ $validCloser = false;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ $type = $this->tokens[$this->tokens[$scopeCloser]['scope_opener']]['type'];
+ $openerType = $this->tokens[$opener]['type'];
+ echo "* closer is not valid (mismatched opener type; $type != $openerType) *".PHP_EOL;
+ }
+ } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo "* closer was valid *".PHP_EOL;
+ }
+ } else {
+ // The closer was not processed, so we need to
+ // complete that token as well.
+ $todo[] = $scopeCloser;
+ }//end if
+
+ if ($validCloser === true) {
+ foreach ($todo as $token) {
+ $this->tokens[$token]['scope_condition'] = $stackPtr;
+ $this->tokens[$token]['scope_opener'] = $opener;
+ $this->tokens[$token]['scope_closer'] = $scopeCloser;
+ }
+
+ if ($this->scopeOpeners[$this->tokens[$stackPtr]['code']]['shared'] === true) {
+ // As we are going back to where we started originally, restore
+ // the ignore value back to its original value.
+ $ignore = $originalIgnore;
+ return $opener;
+ } else if ($scopeCloser === $i
+ && isset($this->scopeOpeners[$tokenType]) === true
+ ) {
+ // Unset scope_condition here or else the token will appear to have
+ // already been processed, and it will be skipped. Normally we want that,
+ // but in this case, the token is both a closer and an opener, so
+ // it needs to act like an opener. This is also why we return the
+ // token before this one; so the closer has a chance to be processed
+ // a second time, but as an opener.
+ unset($this->tokens[$scopeCloser]['scope_condition']);
+ return ($i - 1);
+ } else {
+ return $i;
+ }
+ } else {
+ continue;
+ }//end if
+ }//end if
+ }//end if
+
+ // Is this an opening condition ?
+ if (isset($this->scopeOpeners[$tokenType]) === true) {
+ if ($opener === null) {
+ if ($tokenType === T_USE) {
+ // PHP use keywords are special because they can be
+ // used as blocks but also inline in function definitions.
+ // So if we find them nested inside another opener, just skip them.
+ continue;
+ }
+
+ if ($tokenType === T_FUNCTION
+ && $this->tokens[$stackPtr]['code'] !== T_FUNCTION
+ ) {
+ // Probably a closure, so process it manually.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$stackPtr]['type'];
+ echo str_repeat("\t", $depth);
+ echo "=> Found function before scope opener for $stackPtr:$type, processing manually".PHP_EOL;
+ }
+
+ if (isset($this->tokens[$i]['scope_closer']) === true) {
+ // We've already processed this closure.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo '* already processed, skipping *'.PHP_EOL;
+ }
+
+ $i = $this->tokens[$i]['scope_closer'];
+ continue;
+ }
+
+ $i = self::recurseScopeMap($i, ($depth + 1), $ignore);
+ continue;
+ }//end if
+
+ // Found another opening condition but still haven't
+ // found our opener, so we are never going to find one.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$stackPtr]['type'];
+ echo str_repeat("\t", $depth);
+ echo "=> Found new opening condition before scope opener for $stackPtr:$type, ";
+ }
+
+ if (($this->tokens[$stackPtr]['code'] === T_IF
+ || $this->tokens[$stackPtr]['code'] === T_ELSEIF
+ || $this->tokens[$stackPtr]['code'] === T_ELSE)
+ && ($this->tokens[$i]['code'] === T_ELSE
+ || $this->tokens[$i]['code'] === T_ELSEIF)
+ ) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "continuing".PHP_EOL;
+ }
+
+ return ($i - 1);
+ } else {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "backtracking".PHP_EOL;
+ }
+
+ return $stackPtr;
+ }
+ }//end if
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo '* token is an opening condition *'.PHP_EOL;
+ }
+
+ $isShared = ($this->scopeOpeners[$tokenType]['shared'] === true);
+
+ if (isset($this->tokens[$i]['scope_condition']) === true) {
+ // We've been here before.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo '* already processed, skipping *'.PHP_EOL;
+ }
+
+ if ($isShared === false
+ && isset($this->tokens[$i]['scope_closer']) === true
+ ) {
+ $i = $this->tokens[$i]['scope_closer'];
+ }
+
+ continue;
+ } else if ($currType === $tokenType
+ && $isShared === false
+ && $opener === null
+ ) {
+ // We haven't yet found our opener, but we have found another
+ // scope opener which is the same type as us, and we don't
+ // share openers, so we will never find one.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo '* it was another token\'s opener, bailing *'.PHP_EOL;
+ }
+
+ return $stackPtr;
+ } else {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo '* searching for opener *'.PHP_EOL;
+ }
+
+ if (isset($this->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]) === true) {
+ $oldIgnore = $ignore;
+ $ignore = 0;
+ }
+
+ // PHP has a max nesting level for functions. Stop before we hit that limit
+ // because too many loops means we've run into trouble anyway.
+ if ($depth > 50) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo '* reached maximum nesting level; aborting *'.PHP_EOL;
+ }
+
+ throw new RuntimeException('Maximum nesting level reached; file could not be processed');
+ }
+
+ $oldDepth = $depth;
+ if ($isShared === true
+ && isset($this->scopeOpeners[$tokenType]['with'][$currType]) === true
+ ) {
+ // Don't allow the depth to increment because this is
+ // possibly not a true nesting if we are sharing our closer.
+ // This can happen, for example, when a SWITCH has a large
+ // number of CASE statements with the same shared BREAK.
+ $depth--;
+ }
+
+ $i = self::recurseScopeMap($i, ($depth + 1), $ignore);
+ $depth = $oldDepth;
+
+ if (isset($this->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]) === true) {
+ $ignore = $oldIgnore;
+ }
+ }//end if
+ }//end if
+
+ if (isset($this->scopeOpeners[$currType]['start'][$tokenType]) === true
+ && $opener === null
+ ) {
+ if ($tokenType === T_OPEN_CURLY_BRACKET) {
+ if (isset($this->tokens[$stackPtr]['parenthesis_closer']) === true
+ && $i < $this->tokens[$stackPtr]['parenthesis_closer']
+ ) {
+ // We found a curly brace inside the condition of the
+ // current scope opener, so it must be a string offset.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo '* ignoring curly brace inside condition *'.PHP_EOL;
+ }
+
+ $ignore++;
+ } else {
+ // Make sure this is actually an opener and not a
+ // string offset (e.g., $var{0}).
+ for ($x = ($i - 1); $x > 0; $x--) {
+ if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === true) {
+ continue;
+ } else {
+ // If the first non-whitespace/comment token looks like this
+ // brace is a string offset, or this brace is mid-way through
+ // a new statement, it isn't a scope opener.
+ $disallowed = Util\Tokens::$assignmentTokens;
+ $disallowed += array(
+ T_VARIABLE => true,
+ T_OBJECT_OPERATOR => true,
+ T_COMMA => true,
+ T_OPEN_PARENTHESIS => true,
+ );
+
+ if (isset($disallowed[$this->tokens[$x]['code']]) === true) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo '* ignoring curly brace *'.PHP_EOL;
+ }
+
+ $ignore++;
+ }
+
+ break;
+ }//end if
+ }//end for
+ }//end if
+ }//end if
+
+ if ($ignore === 0 || $tokenType !== T_OPEN_CURLY_BRACKET) {
+ // We found the opening scope token for $currType.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$stackPtr]['type'];
+ echo str_repeat("\t", $depth);
+ echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
+ }
+
+ $opener = $i;
+ }
+ } else if ($tokenType === T_OPEN_PARENTHESIS) {
+ if (isset($this->tokens[$i]['parenthesis_owner']) === true) {
+ $owner = $this->tokens[$i]['parenthesis_owner'];
+ if (isset(Util\Tokens::$scopeOpeners[$this->tokens[$owner]['code']]) === true
+ && isset($this->tokens[$i]['parenthesis_closer']) === true
+ ) {
+ // If we get into here, then we opened a parenthesis for
+ // a scope (eg. an if or else if) so we need to update the
+ // start of the line so that when we check to see
+ // if the closing parenthesis is more than 3 lines away from
+ // the statement, we check from the closing parenthesis.
+ $startLine = $this->tokens[$this->tokens[$i]['parenthesis_closer']]['line'];
+ }
+ }
+ } else if ($tokenType === T_OPEN_CURLY_BRACKET && $opener !== null) {
+ // We opened something that we don't have a scope opener for.
+ // Examples of this are curly brackets for string offsets etc.
+ // We want to ignore this so that we don't have an invalid scope
+ // map.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo '* ignoring curly brace *'.PHP_EOL;
+ }
+
+ $ignore++;
+ } else if ($tokenType === T_CLOSE_CURLY_BRACKET && $ignore > 0) {
+ // We found the end token for the opener we were ignoring.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo '* finished ignoring curly brace *'.PHP_EOL;
+ }
+
+ $ignore--;
+ } else if ($opener === null
+ && isset($this->scopeOpeners[$currType]) === true
+ ) {
+ // If we still haven't found the opener after 3 lines,
+ // we're not going to find it, unless we know it requires
+ // an opener (in which case we better keep looking) or the last
+ // token was empty (in which case we'll just confirm there is
+ // more code in this file and not just a big comment).
+ if ($this->tokens[$i]['line'] >= ($startLine + 3)
+ && isset(Util\Tokens::$emptyTokens[$this->tokens[($i - 1)]['code']]) === false
+ ) {
+ if ($this->scopeOpeners[$currType]['strict'] === true) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$stackPtr]['type'];
+ $lines = ($this->tokens[$i]['line'] - $startLine);
+ echo str_repeat("\t", $depth);
+ echo "=> Still looking for $stackPtr:$type scope opener after $lines lines".PHP_EOL;
+ }
+ } else {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$stackPtr]['type'];
+ echo str_repeat("\t", $depth);
+ echo "=> Couldn't find scope opener for $stackPtr:$type, bailing".PHP_EOL;
+ }
+
+ return $stackPtr;
+ }
+ }
+ } else if ($opener !== null
+ && $tokenType !== T_BREAK
+ && isset($this->endScopeTokens[$tokenType]) === true
+ ) {
+ if (isset($this->tokens[$i]['scope_condition']) === false) {
+ if ($ignore > 0) {
+ // We found the end token for the opener we were ignoring.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", $depth);
+ echo '* finished ignoring curly brace *'.PHP_EOL;
+ }
+
+ $ignore--;
+ } else {
+ // We found a token that closes the scope but it doesn't
+ // have a condition, so it belongs to another token and
+ // our token doesn't have a closer, so pretend this is
+ // the closer.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$stackPtr]['type'];
+ echo str_repeat("\t", $depth);
+ echo "=> Found (unexpected) scope closer for $stackPtr:$type".PHP_EOL;
+ }
+
+ foreach (array($stackPtr, $opener) as $token) {
+ $this->tokens[$token]['scope_condition'] = $stackPtr;
+ $this->tokens[$token]['scope_opener'] = $opener;
+ $this->tokens[$token]['scope_closer'] = $i;
+ }
+
+ return ($i - 1);
+ }//end if
+ }//end if
+ }//end if
+ }//end for
+
+ return $stackPtr;
+
+ }//end recurseScopeMap()
+
+
+ /**
+ * Constructs the level map.
+ *
+ * The level map adds a 'level' index to each token which indicates the
+ * depth that a token within a set of scope blocks. It also adds a
+ * 'condition' index which is an array of the scope conditions that opened
+ * each of the scopes - position 0 being the first scope opener.
+ *
+ * @return void
+ */
+ private function createLevelMap()
+ {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** START LEVEL MAP ***".PHP_EOL;
+ }
+
+ $this->numTokens = count($this->tokens);
+ $level = 0;
+ $conditions = array();
+ $lastOpener = null;
+ $openers = array();
+
+ for ($i = 0; $i < $this->numTokens; $i++) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$i]['type'];
+ $line = $this->tokens[$i]['line'];
+ $len = $this->tokens[$i]['length'];
+ $col = $this->tokens[$i]['column'];
+
+ $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
+
+ echo str_repeat("\t", ($level + 1));
+ echo "Process token $i on line $line [col:$col;len:$len;lvl:$level;";
+ if (empty($conditions) !== true) {
+ $condString = 'conds;';
+ foreach ($conditions as $condition) {
+ $condString .= Util\Tokens::tokenName($condition).',';
+ }
+
+ echo rtrim($condString, ',').';';
+ }
+
+ echo "]: $type => $content".PHP_EOL;
+ }//end if
+
+ $this->tokens[$i]['level'] = $level;
+ $this->tokens[$i]['conditions'] = $conditions;
+
+ if (isset($this->tokens[$i]['scope_condition']) === true) {
+ // Check to see if this token opened the scope.
+ if ($this->tokens[$i]['scope_opener'] === $i) {
+ $stackPtr = $this->tokens[$i]['scope_condition'];
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$stackPtr]['type'];
+ echo str_repeat("\t", ($level + 1));
+ echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
+ }
+
+ $stackPtr = $this->tokens[$i]['scope_condition'];
+
+ // If we find a scope opener that has a shared closer,
+ // then we need to go back over the condition map that we
+ // just created and fix ourselves as we just added some
+ // conditions where there was none. This happens for T_CASE
+ // statements that are using the same break statement.
+ if ($lastOpener !== null && $this->tokens[$lastOpener]['scope_closer'] === $this->tokens[$i]['scope_closer']) {
+ // This opener shares its closer with the previous opener,
+ // but we still need to check if the two openers share their
+ // closer with each other directly (like CASE and DEFAULT)
+ // or if they are just sharing because one doesn't have a
+ // closer (like CASE with no BREAK using a SWITCHes closer).
+ $thisType = $this->tokens[$this->tokens[$i]['scope_condition']]['code'];
+ $opener = $this->tokens[$lastOpener]['scope_condition'];
+
+ $isShared = isset($this->scopeOpeners[$thisType]['with'][$this->tokens[$opener]['code']]);
+
+ reset($this->scopeOpeners[$thisType]['end']);
+ reset($this->scopeOpeners[$this->tokens[$opener]['code']]['end']);
+ $sameEnd = (current($this->scopeOpeners[$thisType]['end']) === current($this->scopeOpeners[$this->tokens[$opener]['code']]['end']));
+
+ if ($isShared === true && $sameEnd === true) {
+ $badToken = $opener;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$badToken]['type'];
+ echo str_repeat("\t", ($level + 1));
+ echo "* shared closer, cleaning up $badToken:$type *".PHP_EOL;
+ }
+
+ for ($x = $this->tokens[$i]['scope_condition']; $x <= $i; $x++) {
+ $oldConditions = $this->tokens[$x]['conditions'];
+ $oldLevel = $this->tokens[$x]['level'];
+ $this->tokens[$x]['level']--;
+ unset($this->tokens[$x]['conditions'][$badToken]);
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$x]['type'];
+ $oldConds = '';
+ foreach ($oldConditions as $condition) {
+ $oldConds .= Util\Tokens::tokenName($condition).',';
+ }
+
+ $oldConds = rtrim($oldConds, ',');
+
+ $newConds = '';
+ foreach ($this->tokens[$x]['conditions'] as $condition) {
+ $newConds .= Util\Tokens::tokenName($condition).',';
+ }
+
+ $newConds = rtrim($newConds, ',');
+
+ $newLevel = $this->tokens[$x]['level'];
+ echo str_repeat("\t", ($level + 1));
+ echo "* cleaned $x:$type *".PHP_EOL;
+ echo str_repeat("\t", ($level + 2));
+ echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
+ echo str_repeat("\t", ($level + 2));
+ echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
+ }//end if
+ }//end for
+
+ unset($conditions[$badToken]);
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$badToken]['type'];
+ echo str_repeat("\t", ($level + 1));
+ echo "* token $badToken:$type removed from conditions array *".PHP_EOL;
+ }
+
+ unset($openers[$lastOpener]);
+
+ $level--;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", ($level + 2));
+ echo '* level decreased *'.PHP_EOL;
+ }
+ }//end if
+ }//end if
+
+ $level++;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", ($level + 1));
+ echo '* level increased *'.PHP_EOL;
+ }
+
+ $conditions[$stackPtr] = $this->tokens[$stackPtr]['code'];
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$stackPtr]['type'];
+ echo str_repeat("\t", ($level + 1));
+ echo "* token $stackPtr:$type added to conditions array *".PHP_EOL;
+ }
+
+ $lastOpener = $this->tokens[$i]['scope_opener'];
+ if ($lastOpener !== null) {
+ $openers[$lastOpener] = $lastOpener;
+ }
+ } else if ($lastOpener !== null && $this->tokens[$lastOpener]['scope_closer'] === $i) {
+ foreach (array_reverse($openers) as $opener) {
+ if ($this->tokens[$opener]['scope_closer'] === $i) {
+ $oldOpener = array_pop($openers);
+ if (empty($openers) === false) {
+ $lastOpener = array_pop($openers);
+ $openers[$lastOpener] = $lastOpener;
+ } else {
+ $lastOpener = null;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$oldOpener]['type'];
+ echo str_repeat("\t", ($level + 1));
+ echo "=> Found scope closer for $oldOpener:$type".PHP_EOL;
+ }
+
+ $oldCondition = array_pop($conditions);
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", ($level + 1));
+ echo '* token '.Util\Tokens::tokenName($oldCondition).' removed from conditions array *'.PHP_EOL;
+ }
+
+ // Make sure this closer actually belongs to us.
+ // Either the condition also has to think this is the
+ // closer, or it has to allow sharing with us.
+ $condition = $this->tokens[$this->tokens[$i]['scope_condition']]['code'];
+ if ($condition !== $oldCondition) {
+ if (isset($this->scopeOpeners[$oldCondition]['with'][$condition]) === false) {
+ $badToken = $this->tokens[$oldOpener]['scope_condition'];
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = Util\Tokens::tokenName($oldCondition);
+ echo str_repeat("\t", ($level + 1));
+ echo "* scope closer was bad, cleaning up $badToken:$type *".PHP_EOL;
+ }
+
+ for ($x = ($oldOpener + 1); $x <= $i; $x++) {
+ $oldConditions = $this->tokens[$x]['conditions'];
+ $oldLevel = $this->tokens[$x]['level'];
+ $this->tokens[$x]['level']--;
+ unset($this->tokens[$x]['conditions'][$badToken]);
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = $this->tokens[$x]['type'];
+ $oldConds = '';
+ foreach ($oldConditions as $condition) {
+ $oldConds .= Util\Tokens::tokenName($condition).',';
+ }
+
+ $oldConds = rtrim($oldConds, ',');
+
+ $newConds = '';
+ foreach ($this->tokens[$x]['conditions'] as $condition) {
+ $newConds .= Util\Tokens::tokenName($condition).',';
+ }
+
+ $newConds = rtrim($newConds, ',');
+
+ $newLevel = $this->tokens[$x]['level'];
+ echo str_repeat("\t", ($level + 1));
+ echo "* cleaned $x:$type *".PHP_EOL;
+ echo str_repeat("\t", ($level + 2));
+ echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
+ echo str_repeat("\t", ($level + 2));
+ echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
+ }//end if
+ }//end for
+ }//end if
+ }//end if
+
+ $level--;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo str_repeat("\t", ($level + 2));
+ echo '* level decreased *'.PHP_EOL;
+ }
+
+ $this->tokens[$i]['level'] = $level;
+ $this->tokens[$i]['conditions'] = $conditions;
+ }//end if
+ }//end foreach
+ }//end if
+ }//end if
+ }//end for
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t*** END LEVEL MAP ***".PHP_EOL;
+ }
+
+ }//end createLevelMap()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Function for caching between runs.
+ *
+ * @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\Util;
+
+use PHP_CodeSniffer\Autoload;
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Ruleset;
+use PHP_CodeSniffer\Util\Common;
+
+class Cache
+{
+
+ /**
+ * The filesystem location of the cache file.
+ *
+ * @var void
+ */
+ private static $path = '';
+
+ /**
+ * The cached data.
+ *
+ * @var array<string, mixed>
+ */
+ private static $cache = array();
+
+
+ /**
+ * Loads existing cache data for the run, if any.
+ *
+ * @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 static function load(Ruleset $ruleset, Config $config)
+ {
+ // Look at every loaded sniff class so far and use their file contents
+ // to generate a hash for the code used during the run.
+ // At this point, the loaded class list contains the core PHPCS code
+ // and all sniffs that have been loaded as part of the run.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo PHP_EOL."\tGenerating loaded file list for code hash".PHP_EOL;
+ }
+
+ $codeHash = '';
+ $classes = array_keys(Autoload::getLoadedClasses());
+ sort($classes);
+
+ $installDir = dirname(__DIR__);
+ $installDirLen = strlen($installDir);
+ $standardDir = $installDir.DIRECTORY_SEPARATOR.'Standards';
+ $standardDirLen = strlen($standardDir);
+ foreach ($classes as $file) {
+ if (substr($file, 0, $standardDirLen) !== $standardDir) {
+ if (substr($file, 0, $installDirLen) === $installDir) {
+ // We are only interested in sniffs here.
+ continue;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t=> external file: $file".PHP_EOL;
+ }
+ } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t=> internal sniff: $file".PHP_EOL;
+ }
+
+ $codeHash .= md5_file($file);
+ }
+
+ // Add the content of the used rulesets to the hash so that sniff setting
+ // changes in the ruleset invalidate the cache.
+ $rulesets = $ruleset->paths;
+ sort($rulesets);
+ foreach ($rulesets as $file) {
+ if (substr($file, 0, $standardDirLen) !== $standardDir) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t=> external ruleset: $file".PHP_EOL;
+ }
+ } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t=> internal ruleset: $file".PHP_EOL;
+ }
+
+ $codeHash .= md5_file($file);
+ }
+
+ // Go through the core PHPCS code and add those files to the file
+ // hash. This ensures that core PHPCS changes will also invalidate the cache.
+ // Note that we ignore sniffs here, and any files that don't affect
+ // the outcome of the run.
+ $di = new \RecursiveIteratorIterator(
+ new \RecursiveDirectoryIterator($installDir),
+ 0,
+ \RecursiveIteratorIterator::CATCH_GET_CHILD
+ );
+
+ $di = new \RecursiveDirectoryIterator($installDir);
+ $filter = new \RecursiveCallbackFilterIterator(
+ $di,
+ function ($file, $key, $iterator) {
+ // Skip hidden files.
+ $filename = $file->getFilename();
+ if (substr($filename, 0, 1) === '.') {
+ return false;
+ }
+
+ $filePath = Common::realpath($file->getPathname());
+ if ($filePath === false) {
+ return false;
+ }
+
+ if (is_dir($filePath) === true
+ && ($filename === 'Standards'
+ || $filename === 'Exceptions'
+ || $filename === 'Reports'
+ || $filename === 'Generators')
+ ) {
+ return false;
+ }
+
+ return true;
+ }
+ );
+
+ $iterator = new \RecursiveIteratorIterator($filter);
+ foreach ($iterator as $file) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t=> core file: $file".PHP_EOL;
+ }
+
+ $codeHash .= md5_file($file);
+ }
+
+ $codeHash = md5($codeHash);
+
+ // Along with the code hash, use various settings that can affect
+ // the results of a run to create a new hash. This hash will be used
+ // in the cache file name.
+ $rulesetHash = md5(var_export($ruleset->ignorePatterns, true).var_export($ruleset->includePatterns, true));
+ $configData = array(
+ 'tabWidth' => $config->tabWidth,
+ 'encoding' => $config->encoding,
+ 'recordErrors' => $config->recordErrors,
+ 'annotations' => $config->annotations,
+ 'codeHash' => $codeHash,
+ 'rulesetHash' => $rulesetHash,
+ );
+
+ $configString = implode(',', $configData);
+ $cacheHash = substr(sha1($configString), 0, 12);
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\tGenerating cache key data".PHP_EOL;
+ echo "\t\t=> tabWidth: ".$configData['tabWidth'].PHP_EOL;
+ echo "\t\t=> encoding: ".$configData['encoding'].PHP_EOL;
+ echo "\t\t=> recordErrors: ".(int) $configData['recordErrors'].PHP_EOL;
+ echo "\t\t=> annotations: ".(int) $configData['annotations'].PHP_EOL;
+ echo "\t\t=> codeHash: ".$configData['codeHash'].PHP_EOL;
+ echo "\t\t=> rulesetHash: ".$configData['rulesetHash'].PHP_EOL;
+ echo "\t\t=> cacheHash: $cacheHash".PHP_EOL;
+ }
+
+ if ($config->cacheFile !== null) {
+ $cacheFile = $config->cacheFile;
+ } else {
+ // Determine the common paths for all files being checked.
+ // We can use this to locate an existing cache file, or to
+ // determine where to create a new one.
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\tChecking possible cache file paths".PHP_EOL;
+ }
+
+ $paths = array();
+ foreach ($config->files as $file) {
+ $file = Common::realpath($file);
+ while ($file !== DIRECTORY_SEPARATOR) {
+ if (isset($paths[$file]) === false) {
+ $paths[$file] = 1;
+ } else {
+ $paths[$file]++;
+ }
+
+ $lastFile = $file;
+ $file = dirname($file);
+ if ($file === $lastFile) {
+ // Just in case something went wrong,
+ // we don't want to end up in an infinite loop.
+ break;
+ }
+ }
+ }
+
+ ksort($paths);
+ $paths = array_reverse($paths);
+
+ $numFiles = count($config->files);
+ $tmpDir = sys_get_temp_dir();
+ $cacheFile = null;
+ foreach ($paths as $file => $count) {
+ if ($count !== $numFiles) {
+ unset($paths[$file]);
+ continue;
+ }
+
+ $fileHash = substr(sha1($file), 0, 12);
+ $testFile = $tmpDir.DIRECTORY_SEPARATOR."phpcs.$fileHash.$cacheHash.cache";
+ if ($cacheFile === null) {
+ // This will be our default location if we can't find
+ // an existing file.
+ $cacheFile = $testFile;
+ }
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t=> $testFile".PHP_EOL;
+ echo "\t\t\t * based on shared location: $file *".PHP_EOL;
+ }
+
+ if (file_exists($testFile) === true) {
+ $cacheFile = $testFile;
+ break;
+ }
+ }//end foreach
+
+ if ($cacheFile === null) {
+ // Unlikely, but just in case $paths is empty for some reason.
+ $cacheFile = $tmpDir.DIRECTORY_SEPARATOR."phpcs.$cacheHash.cache";
+ }
+ }//end if
+
+ self::$path = $cacheFile;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t=> Using cache file: ".self::$path.PHP_EOL;
+ }
+
+ if (file_exists(self::$path) === true) {
+ self::$cache = json_decode(file_get_contents(self::$path), true);
+
+ // Verify the contents of the cache file.
+ if (self::$cache['config'] !== $configData) {
+ self::$cache = array();
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t* cache was invalid and has been cleared *".PHP_EOL;
+ }
+ }
+ } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t* cache file does not exist *".PHP_EOL;
+ }
+
+ self::$cache['config'] = $configData;
+
+ }//end load()
+
+
+ /**
+ * Saves the current cache to the filesystem.
+ *
+ * @return void
+ */
+ public static function save()
+ {
+ file_put_contents(self::$path, json_encode(self::$cache));
+
+ }//end save()
+
+
+ /**
+ * Retrieves a single entry from the cache.
+ *
+ * @param string $key The key of the data to get. If NULL,
+ * everything in the cache is returned.
+ *
+ * @return mixed
+ */
+ public static function get($key=null)
+ {
+ if ($key === null) {
+ return self::$cache;
+ }
+
+ if (isset(self::$cache[$key]) === true) {
+ return self::$cache[$key];
+ }
+
+ return false;
+
+ }//end get()
+
+
+ /**
+ * Retrieves a single entry from the cache.
+ *
+ * @param string $key The key of the data to set. If NULL,
+ * sets the entire cache.
+ * @param mixed $value The value to set.
+ *
+ * @return void
+ */
+ public static function set($key, $value)
+ {
+ if ($key === null) {
+ self::$cache = $value;
+ } else {
+ self::$cache[$key] = $value;
+ }
+
+ }//end set()
+
+
+ /**
+ * Retrieves the number of cache entries.
+ *
+ * @return int
+ */
+ public static function getSize()
+ {
+ return (count(self::$cache) - 1);
+
+ }//end getSize()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Basic util functions.
+ *
+ * @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\Util;
+
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+
+class Common
+{
+
+ /**
+ * An array of variable types for param/var we will check.
+ *
+ * @var string[]
+ */
+ public static $allowedTypes = array(
+ 'array',
+ 'boolean',
+ 'float',
+ 'integer',
+ 'mixed',
+ 'object',
+ 'string',
+ 'resource',
+ 'callable',
+ );
+
+
+ /**
+ * Return TRUE if the path is a PHAR file.
+ *
+ * @param string $path The path to use.
+ *
+ * @return mixed
+ */
+ public static function isPharFile($path)
+ {
+ if (strpos($path, 'phar://') === 0) {
+ return true;
+ }
+
+ return false;
+
+ }//end isPharFile()
+
+
+ /**
+ * CodeSniffer alternative for realpath.
+ *
+ * Allows for PHAR support.
+ *
+ * @param string $path The path to use.
+ *
+ * @return mixed
+ */
+ public static function realpath($path)
+ {
+ // Support the path replacement of ~ with the user's home directory.
+ if (substr($path, 0, 2) === '~/') {
+ $homeDir = getenv('HOME');
+ if ($homeDir !== false) {
+ $path = $homeDir.substr($path, 1);
+ }
+ }
+
+ // No extra work needed if this is not a phar file.
+ if (self::isPharFile($path) === false) {
+ return realpath($path);
+ }
+
+ // Before trying to break down the file path,
+ // check if it exists first because it will mostly not
+ // change after running the below code.
+ if (file_exists($path) === true) {
+ return $path;
+ }
+
+ $phar = \Phar::running(false);
+ $extra = str_replace('phar://'.$phar, '', $path);
+ $path = realpath($phar);
+ if ($path === false) {
+ return false;
+ }
+
+ $path = 'phar://'.$path.$extra;
+ if (file_exists($path) === true) {
+ return $path;
+ }
+
+ return false;
+
+ }//end realpath()
+
+
+ /**
+ * Removes a base path from the front of a file path.
+ *
+ * @param string $path The path of the file.
+ * @param string $basepath The base path to remove. This should not end
+ * with a directory separator.
+ *
+ * @return string
+ */
+ public static function stripBasepath($path, $basepath)
+ {
+ if (empty($basepath) === true) {
+ return $path;
+ }
+
+ $basepathLen = strlen($basepath);
+ if (substr($path, 0, $basepathLen) === $basepath) {
+ $path = substr($path, $basepathLen);
+ }
+
+ $path = ltrim($path, DIRECTORY_SEPARATOR);
+ if ($path === '') {
+ $path = '.';
+ }
+
+ return $path;
+
+ }//end stripBasepath()
+
+
+ /**
+ * Detects the EOL character being used in a string.
+ *
+ * @param string $contents The contents to check.
+ *
+ * @return string
+ */
+ public static function detectLineEndings($contents)
+ {
+ if (preg_match("/\r\n?|\n/", $contents, $matches) !== 1) {
+ // Assume there are no newlines.
+ $eolChar = "\n";
+ } else {
+ $eolChar = $matches[0];
+ }
+
+ return $eolChar;
+
+ }//end detectLineEndings()
+
+
+ /**
+ * Prepares token content for output to screen.
+ *
+ * Replaces invisible characters so they are visible. On non-Windows
+ * OSes it will also colour the invisible characters.
+ *
+ * @param string $content The content to prepare.
+ * @param string[] $exclude A list of characters to leave invisible.
+ * Can contain \r, \n, \t and a space.
+ *
+ * @return string
+ */
+ public static function prepareForOutput($content, $exclude=array())
+ {
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+ if (in_array("\r", $exclude) === false) {
+ $content = str_replace("\r", '\r', $content);
+ }
+
+ if (in_array("\n", $exclude) === false) {
+ $content = str_replace("\n", '\n', $content);
+ }
+
+ if (in_array("\t", $exclude) === false) {
+ $content = str_replace("\t", '\t', $content);
+ }
+ } else {
+ if (in_array("\r", $exclude) === false) {
+ $content = str_replace("\r", "\033[30;1m\\r\033[0m", $content);
+ }
+
+ if (in_array("\n", $exclude) === false) {
+ $content = str_replace("\n", "\033[30;1m\\n\033[0m", $content);
+ }
+
+ if (in_array("\t", $exclude) === false) {
+ $content = str_replace("\t", "\033[30;1m\\t\033[0m", $content);
+ }
+
+ if (in_array(' ', $exclude) === false) {
+ $content = str_replace(' ', "\033[30;1m·\033[0m", $content);
+ }
+ }//end if
+
+ return $content;
+
+ }//end prepareForOutput()
+
+
+ /**
+ * Returns true if the specified string is in the camel caps format.
+ *
+ * @param string $string The string the verify.
+ * @param boolean $classFormat If true, check to see if the string is in the
+ * class format. Class format strings must start
+ * with a capital letter and contain no
+ * underscores.
+ * @param boolean $public If true, the first character in the string
+ * must be an a-z character. If false, the
+ * character must be an underscore. This
+ * argument is only applicable if $classFormat
+ * is false.
+ * @param boolean $strict If true, the string must not have two capital
+ * letters next to each other. If false, a
+ * relaxed camel caps policy is used to allow
+ * for acronyms.
+ *
+ * @return boolean
+ */
+ public static function isCamelCaps(
+ $string,
+ $classFormat=false,
+ $public=true,
+ $strict=true
+ ) {
+ // Check the first character first.
+ if ($classFormat === false) {
+ $legalFirstChar = '';
+ if ($public === false) {
+ $legalFirstChar = '[_]';
+ }
+
+ if ($strict === false) {
+ // Can either start with a lowercase letter, or multiple uppercase
+ // in a row, representing an acronym.
+ $legalFirstChar .= '([A-Z]{2,}|[a-z])';
+ } else {
+ $legalFirstChar .= '[a-z]';
+ }
+ } else {
+ $legalFirstChar = '[A-Z]';
+ }
+
+ if (preg_match("/^$legalFirstChar/", $string) === 0) {
+ return false;
+ }
+
+ // Check that the name only contains legal characters.
+ $legalChars = 'a-zA-Z0-9';
+ if (preg_match("|[^$legalChars]|", substr($string, 1)) > 0) {
+ return false;
+ }
+
+ if ($strict === true) {
+ // Check that there are not two capital letters next to each other.
+ $length = strlen($string);
+ $lastCharWasCaps = $classFormat;
+
+ for ($i = 1; $i < $length; $i++) {
+ $ascii = ord($string{$i});
+ if ($ascii >= 48 && $ascii <= 57) {
+ // The character is a number, so it cant be a capital.
+ $isCaps = false;
+ } else {
+ if (strtoupper($string{$i}) === $string{$i}) {
+ $isCaps = true;
+ } else {
+ $isCaps = false;
+ }
+ }
+
+ if ($isCaps === true && $lastCharWasCaps === true) {
+ return false;
+ }
+
+ $lastCharWasCaps = $isCaps;
+ }
+ }//end if
+
+ return true;
+
+ }//end isCamelCaps()
+
+
+ /**
+ * Returns true if the specified string is in the underscore caps format.
+ *
+ * @param string $string The string to verify.
+ *
+ * @return boolean
+ */
+ public static function isUnderscoreName($string)
+ {
+ // If there are space in the name, it can't be valid.
+ if (strpos($string, ' ') !== false) {
+ return false;
+ }
+
+ $validName = true;
+ $nameBits = explode('_', $string);
+
+ if (preg_match('|^[A-Z]|', $string) === 0) {
+ // Name does not begin with a capital letter.
+ $validName = false;
+ } else {
+ foreach ($nameBits as $bit) {
+ if ($bit === '') {
+ continue;
+ }
+
+ if ($bit{0} !== strtoupper($bit{0})) {
+ $validName = false;
+ break;
+ }
+ }
+ }
+
+ return $validName;
+
+ }//end isUnderscoreName()
+
+
+ /**
+ * Returns a valid variable type for param/var tag.
+ *
+ * If type is not one of the standard type, it must be a custom type.
+ * Returns the correct type name suggestion if type name is invalid.
+ *
+ * @param string $varType The variable type to process.
+ *
+ * @return string
+ */
+ public static function suggestType($varType)
+ {
+ if ($varType === '') {
+ return '';
+ }
+
+ if (in_array($varType, self::$allowedTypes) === true) {
+ return $varType;
+ } else {
+ $lowerVarType = strtolower($varType);
+ switch ($lowerVarType) {
+ case 'bool':
+ case 'boolean':
+ return 'boolean';
+ case 'double':
+ case 'real':
+ case 'float':
+ return 'float';
+ case 'int':
+ case 'integer':
+ return 'integer';
+ case 'array()':
+ case 'array':
+ return 'array';
+ }//end switch
+
+ if (strpos($lowerVarType, 'array(') !== false) {
+ // Valid array declaration:
+ // array, array(type), array(type1 => type2).
+ $matches = array();
+ $pattern = '/^array\(\s*([^\s^=^>]*)(\s*=>\s*(.*))?\s*\)/i';
+ if (preg_match($pattern, $varType, $matches) !== 0) {
+ $type1 = '';
+ if (isset($matches[1]) === true) {
+ $type1 = $matches[1];
+ }
+
+ $type2 = '';
+ if (isset($matches[3]) === true) {
+ $type2 = $matches[3];
+ }
+
+ $type1 = self::suggestType($type1);
+ $type2 = self::suggestType($type2);
+ if ($type2 !== '') {
+ $type2 = ' => '.$type2;
+ }
+
+ return "array($type1$type2)";
+ } else {
+ return 'array';
+ }//end if
+ } else if (in_array($lowerVarType, self::$allowedTypes) === true) {
+ // A valid type, but not lower cased.
+ return $lowerVarType;
+ } else {
+ // Must be a custom type name.
+ return $varType;
+ }//end if
+ }//end if
+
+ }//end suggestType()
+
+
+ /**
+ * Given a sniff class name, returns the code for the sniff.
+ *
+ * @param string $sniffClass The fully qualified sniff class name.
+ *
+ * @return string
+ */
+ public static function getSniffCode($sniffClass)
+ {
+ $parts = explode('\\', $sniffClass);
+ $sniff = array_pop($parts);
+
+ if (substr($sniff, -5) === 'Sniff') {
+ // Sniff class name.
+ $sniff = substr($sniff, 0, -5);
+ } else {
+ // Unit test class name.
+ $sniff = substr($sniff, 0, -8);
+ }
+
+ $category = array_pop($parts);
+ $sniffDir = array_pop($parts);
+ $standard = array_pop($parts);
+ $code = $standard.'.'.$category.'.'.$sniff;
+ return $code;
+
+ }//end getSniffCode()
+
+
+ /**
+ * Removes project-specific information from a sniff class name.
+ *
+ * @param string $sniffClass The fully qualified sniff class name.
+ *
+ * @return string
+ */
+ public static function cleanSniffClass($sniffClass)
+ {
+ $newName = strtolower($sniffClass);
+
+ $sniffPos = strrpos($newName, '\sniffs\\');
+ if ($sniffPos === false) {
+ // Nothing we can do as it isn't in a known format.
+ return $newName;
+ }
+
+ $end = (strlen($newName) - $sniffPos + 1);
+ $start = strrpos($newName, '\\', ($end * -1));
+
+ if ($start === false) {
+ // Nothing needs to be cleaned.
+ return $newName;
+ }
+
+ $newName = substr($newName, ($start + 1));
+ return $newName;
+
+ }//end cleanSniffClass()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Functions for helping process standards.
+ *
+ * @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\Util;
+
+use PHP_CodeSniffer\Config;
+
+class Standards
+{
+
+
+ /**
+ * Get a list paths where standards are installed.
+ *
+ * @return array
+ */
+ public static function getInstalledStandardPaths()
+ {
+ $ds = DIRECTORY_SEPARATOR;
+
+ $installedPaths = array(dirname(dirname(__DIR__)).$ds.'src'.$ds.'Standards');
+ $configPaths = Config::getConfigData('installed_paths');
+ if ($configPaths !== null) {
+ $installedPaths = array_merge($installedPaths, explode(',', $configPaths));
+ }
+
+ $resolvedInstalledPaths = array();
+ foreach ($installedPaths as $installedPath) {
+ if (substr($installedPath, 0, 1) === '.') {
+ $installedPath = Common::realPath(__DIR__.$ds.'..'.$ds.'..'.$ds.$installedPath);
+ }
+
+ $resolvedInstalledPaths[] = $installedPath;
+ }
+
+ return $resolvedInstalledPaths;
+
+ }//end getInstalledStandardPaths()
+
+
+ /**
+ * Get the details of all coding standards installed.
+ *
+ * Coding standards are directories located in the
+ * CodeSniffer/Standards directory. Valid coding standards
+ * include a Sniffs subdirectory.
+ *
+ * The details returned for each standard are:
+ * - path: the path to the coding standard's main directory
+ * - name: the name of the coding standard, as sourced from the ruleset.xml file
+ * - namespace: the namespace used by the coding standard, as sourced from the ruleset.xml file
+ *
+ * If you only need the paths to the installed standards,
+ * use getInstalledStandardPaths() instead as it performs less work to
+ * retrieve coding standard names.
+ *
+ * @param boolean $includeGeneric If true, the special "Generic"
+ * coding standard will be included
+ * if installed.
+ * @param string $standardsDir A specific directory to look for standards
+ * in. If not specified, PHP_CodeSniffer will
+ * look in its default locations.
+ *
+ * @return array
+ * @see getInstalledStandardPaths()
+ */
+ public static function getInstalledStandardDetails(
+ $includeGeneric=false,
+ $standardsDir=''
+ ) {
+ $rulesets = array();
+
+ if ($standardsDir === '') {
+ $installedPaths = self::getInstalledStandardPaths();
+ } else {
+ $installedPaths = array($standardsDir);
+ }
+
+ foreach ($installedPaths as $standardsDir) {
+ // Check if the installed dir is actually a standard itself.
+ $csFile = $standardsDir.'/ruleset.xml';
+ if (is_file($csFile) === true) {
+ $rulesets[] = $csFile;
+ continue;
+ }
+
+ $di = new \DirectoryIterator($standardsDir);
+ foreach ($di as $file) {
+ if ($file->isDir() === true && $file->isDot() === false) {
+ $filename = $file->getFilename();
+
+ // Ignore the special "Generic" standard.
+ if ($includeGeneric === false && $filename === 'Generic') {
+ continue;
+ }
+
+ // Valid coding standard dirs include a ruleset.
+ $csFile = $file->getPathname().'/ruleset.xml';
+ if (is_file($csFile) === true) {
+ $rulesets[] = $csFile;
+ }
+ }
+ }
+ }//end foreach
+
+ $installedStandards = array();
+
+ foreach ($rulesets as $rulesetPath) {
+ $ruleset = simplexml_load_string(file_get_contents($rulesetPath));
+ if ($ruleset === false) {
+ continue;
+ }
+
+ $standardName = (string) $ruleset['name'];
+ $dirname = basename(dirname($rulesetPath));
+
+ if (isset($ruleset['namespace']) === true) {
+ $namespace = (string) $ruleset['namespace'];
+ } else {
+ $namespace = $dirname;
+ }
+
+ $installedStandards[$dirname] = array(
+ 'path' => dirname($rulesetPath),
+ 'name' => $standardName,
+ 'namespace' => $namespace,
+ );
+ }//end foreach
+
+ return $installedStandards;
+
+ }//end getInstalledStandardDetails()
+
+
+ /**
+ * Get a list of all coding standards installed.
+ *
+ * Coding standards are directories located in the
+ * CodeSniffer/Standards directory. Valid coding standards
+ * include a Sniffs subdirectory.
+ *
+ * @param boolean $includeGeneric If true, the special "Generic"
+ * coding standard will be included
+ * if installed.
+ * @param string $standardsDir A specific directory to look for standards
+ * in. If not specified, PHP_CodeSniffer will
+ * look in its default locations.
+ *
+ * @return array
+ * @see isInstalledStandard()
+ */
+ public static function getInstalledStandards(
+ $includeGeneric=false,
+ $standardsDir=''
+ ) {
+ $installedStandards = array();
+
+ if ($standardsDir === '') {
+ $installedPaths = self::getInstalledStandardPaths();
+ } else {
+ $installedPaths = array($standardsDir);
+ }
+
+ foreach ($installedPaths as $standardsDir) {
+ // Check if the installed dir is actually a standard itself.
+ $csFile = $standardsDir.'/ruleset.xml';
+ if (is_file($csFile) === true) {
+ $installedStandards[] = basename($standardsDir);
+ continue;
+ }
+
+ if (is_dir($standardsDir) === false) {
+ // Doesn't exist.
+ continue;
+ }
+
+ $di = new \DirectoryIterator($standardsDir);
+ foreach ($di as $file) {
+ if ($file->isDir() === true && $file->isDot() === false) {
+ $filename = $file->getFilename();
+
+ // Ignore the special "Generic" standard.
+ if ($includeGeneric === false && $filename === 'Generic') {
+ continue;
+ }
+
+ // Valid coding standard dirs include a ruleset.
+ $csFile = $file->getPathname().'/ruleset.xml';
+ if (is_file($csFile) === true) {
+ $installedStandards[] = $filename;
+ }
+ }
+ }
+ }//end foreach
+
+ return $installedStandards;
+
+ }//end getInstalledStandards()
+
+
+ /**
+ * Determine if a standard is installed.
+ *
+ * Coding standards are directories located in the
+ * CodeSniffer/Standards directory. Valid coding standards
+ * include a ruleset.xml file.
+ *
+ * @param string $standard The name of the coding standard.
+ *
+ * @return boolean
+ * @see getInstalledStandards()
+ */
+ public static function isInstalledStandard($standard)
+ {
+ $path = self::getInstalledStandardPath($standard);
+ if ($path !== null && strpos($path, 'ruleset.xml') !== false) {
+ return true;
+ } else {
+ // This could be a custom standard, installed outside our
+ // standards directory.
+ $standard = Common::realPath($standard);
+
+ // Might be an actual ruleset file itUtil.
+ // If it has an XML extension, let's at least try it.
+ if (is_file($standard) === true
+ && (substr(strtolower($standard), -4) === '.xml'
+ || substr(strtolower($standard), -9) === '.xml.dist')
+ ) {
+ return true;
+ }
+
+ // If it is a directory with a ruleset.xml file in it,
+ // it is a standard.
+ $ruleset = rtrim($standard, ' /\\').DIRECTORY_SEPARATOR.'ruleset.xml';
+ if (is_file($ruleset) === true) {
+ return true;
+ }
+ }//end if
+
+ return false;
+
+ }//end isInstalledStandard()
+
+
+ /**
+ * Return the path of an installed coding standard.
+ *
+ * Coding standards are directories located in the
+ * CodeSniffer/Standards directory. Valid coding standards
+ * include a ruleset.xml file.
+ *
+ * @param string $standard The name of the coding standard.
+ *
+ * @return string|null
+ */
+ public static function getInstalledStandardPath($standard)
+ {
+ if (strpos($standard, '.') !== false) {
+ return null;
+ }
+
+ $installedPaths = self::getInstalledStandardPaths();
+ foreach ($installedPaths as $installedPath) {
+ $standardPath = $installedPath.DIRECTORY_SEPARATOR.$standard;
+ if (file_exists($standardPath) === false) {
+ if (basename($installedPath) !== $standard) {
+ continue;
+ }
+
+ $standardPath = $installedPath;
+ }
+
+ $path = Common::realpath($standardPath.DIRECTORY_SEPARATOR.'ruleset.xml');
+
+ if (is_file($path) === true) {
+ return $path;
+ } else if (Common::isPharFile($standardPath) === true) {
+ $path = Common::realpath($standardPath);
+ if ($path !== false) {
+ return $path;
+ }
+ }
+ }//end foreach
+
+ return null;
+
+ }//end getInstalledStandardPath()
+
+
+ /**
+ * Prints out a list of installed coding standards.
+ *
+ * @return void
+ */
+ public static function printInstalledStandards()
+ {
+ $installedStandards = self::getInstalledStandards();
+ $numStandards = count($installedStandards);
+
+ if ($numStandards === 0) {
+ echo 'No coding standards are installed.'.PHP_EOL;
+ } else {
+ $lastStandard = array_pop($installedStandards);
+ if ($numStandards === 1) {
+ echo "The only coding standard installed is $lastStandard".PHP_EOL;
+ } else {
+ $standardList = implode(', ', $installedStandards);
+ $standardList .= ' and '.$lastStandard;
+ echo 'The installed coding standards are '.$standardList.PHP_EOL;
+ }
+ }
+
+ }//end printInstalledStandards()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Timing functions for the 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\Util;
+
+class Timing
+{
+
+ /**
+ * The start time of the run.
+ *
+ * @var float
+ */
+ private static $startTime;
+
+ /**
+ * Used to make sure we only print the run time once per run.
+ *
+ * @var boolean
+ */
+ private static $printed = false;
+
+
+ /**
+ * Start recording time for the run.
+ *
+ * @return void
+ */
+ public static function startTiming()
+ {
+
+ self::$startTime = microtime(true);
+
+ }//end startTiming()
+
+
+ /**
+ * Print information about the run.
+ *
+ * @param boolean $force If TRUE, prints the output even if it has
+ * already been printed during the run.
+ *
+ * @return void
+ */
+ public static function printRunTime($force=false)
+ {
+ if ($force === false && self::$printed === true) {
+ // A double call.
+ return;
+ }
+
+ if (self::$startTime === null) {
+ // Timing was never started.
+ return;
+ }
+
+ $time = ((microtime(true) - self::$startTime) * 1000);
+
+ if ($time > 60000) {
+ $mins = floor($time / 60000);
+ $secs = round((($time % 60000) / 1000), 2);
+ $time = $mins.' mins';
+ if ($secs !== 0) {
+ $time .= ", $secs secs";
+ }
+ } else if ($time > 1000) {
+ $time = round(($time / 1000), 2).' secs';
+ } else {
+ $time = round($time).'ms';
+ }
+
+ $mem = round((memory_get_peak_usage(true) / (1024 * 1024)), 2).'Mb';
+ echo "Time: $time; Memory: $mem".PHP_EOL.PHP_EOL;
+
+ self::$printed = true;
+
+ }//end printRunTime()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Stores weightings and groupings of tokens.
+ *
+ * @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\Util;
+
+define('T_NONE', 'PHPCS_T_NONE');
+define('T_OPEN_CURLY_BRACKET', 'PHPCS_T_OPEN_CURLY_BRACKET');
+define('T_CLOSE_CURLY_BRACKET', 'PHPCS_T_CLOSE_CURLY_BRACKET');
+define('T_OPEN_SQUARE_BRACKET', 'PHPCS_T_OPEN_SQUARE_BRACKET');
+define('T_CLOSE_SQUARE_BRACKET', 'PHPCS_T_CLOSE_SQUARE_BRACKET');
+define('T_OPEN_PARENTHESIS', 'PHPCS_T_OPEN_PARENTHESIS');
+define('T_CLOSE_PARENTHESIS', 'PHPCS_T_CLOSE_PARENTHESIS');
+define('T_COLON', 'PHPCS_T_COLON');
+define('T_NULLABLE', 'PHPCS_T_NULLABLE');
+define('T_STRING_CONCAT', 'PHPCS_T_STRING_CONCAT');
+define('T_INLINE_THEN', 'PHPCS_T_INLINE_THEN');
+define('T_INLINE_ELSE', 'PHPCS_T_INLINE_ELSE');
+define('T_NULL', 'PHPCS_T_NULL');
+define('T_FALSE', 'PHPCS_T_FALSE');
+define('T_TRUE', 'PHPCS_T_TRUE');
+define('T_SEMICOLON', 'PHPCS_T_SEMICOLON');
+define('T_EQUAL', 'PHPCS_T_EQUAL');
+define('T_MULTIPLY', 'PHPCS_T_MULTIPLY');
+define('T_DIVIDE', 'PHPCS_T_DIVIDE');
+define('T_PLUS', 'PHPCS_T_PLUS');
+define('T_MINUS', 'PHPCS_T_MINUS');
+define('T_MODULUS', 'PHPCS_T_MODULUS');
+define('T_BITWISE_AND', 'PHPCS_T_BITWISE_AND');
+define('T_BITWISE_OR', 'PHPCS_T_BITWISE_OR');
+define('T_BITWISE_XOR', 'PHPCS_T_BITWISE_XOR');
+define('T_ARRAY_HINT', 'PHPCS_T_ARRAY_HINT');
+define('T_GREATER_THAN', 'PHPCS_T_GREATER_THAN');
+define('T_LESS_THAN', 'PHPCS_T_LESS_THAN');
+define('T_BOOLEAN_NOT', 'PHPCS_T_BOOLEAN_NOT');
+define('T_SELF', 'PHPCS_T_SELF');
+define('T_PARENT', 'PHPCS_T_PARENT');
+define('T_DOUBLE_QUOTED_STRING', 'PHPCS_T_DOUBLE_QUOTED_STRING');
+define('T_COMMA', 'PHPCS_T_COMMA');
+define('T_HEREDOC', 'PHPCS_T_HEREDOC');
+define('T_PROTOTYPE', 'PHPCS_T_PROTOTYPE');
+define('T_THIS', 'PHPCS_T_THIS');
+define('T_REGULAR_EXPRESSION', 'PHPCS_T_REGULAR_EXPRESSION');
+define('T_PROPERTY', 'PHPCS_T_PROPERTY');
+define('T_LABEL', 'PHPCS_T_LABEL');
+define('T_OBJECT', 'PHPCS_T_OBJECT');
+define('T_CLOSE_OBJECT', 'PHPCS_T_CLOSE_OBJECT');
+define('T_COLOUR', 'PHPCS_T_COLOUR');
+define('T_HASH', 'PHPCS_T_HASH');
+define('T_URL', 'PHPCS_T_URL');
+define('T_STYLE', 'PHPCS_T_STYLE');
+define('T_ASPERAND', 'PHPCS_T_ASPERAND');
+define('T_DOLLAR', 'PHPCS_T_DOLLAR');
+define('T_TYPEOF', 'PHPCS_T_TYPEOF');
+define('T_CLOSURE', 'PHPCS_T_CLOSURE');
+define('T_ANON_CLASS', 'PHPCS_T_ANON_CLASS');
+define('T_BACKTICK', 'PHPCS_T_BACKTICK');
+define('T_START_NOWDOC', 'PHPCS_T_START_NOWDOC');
+define('T_NOWDOC', 'PHPCS_T_NOWDOC');
+define('T_END_NOWDOC', 'PHPCS_T_END_NOWDOC');
+define('T_OPEN_SHORT_ARRAY', 'PHPCS_T_OPEN_SHORT_ARRAY');
+define('T_CLOSE_SHORT_ARRAY', 'PHPCS_T_CLOSE_SHORT_ARRAY');
+define('T_GOTO_LABEL', 'PHPCS_T_GOTO_LABEL');
+define('T_BINARY_CAST', 'PHPCS_T_BINARY_CAST');
+define('T_EMBEDDED_PHP', 'PHPCS_T_EMBEDDED_PHP');
+define('T_RETURN_TYPE', 'PHPCS_T_RETURN_TYPE');
+define('T_OPEN_USE_GROUP', 'PHPCS_T_OPEN_USE_GROUP');
+define('T_CLOSE_USE_GROUP', 'PHPCS_T_CLOSE_USE_GROUP');
+define('T_ZSR', 'PHPCS_T_ZSR');
+define('T_ZSR_EQUAL', 'PHPCS_T_ZSR_EQUAL');
+
+// Some PHP 5.5 tokens, replicated for lower versions.
+if (defined('T_FINALLY') === false) {
+ define('T_FINALLY', 'PHPCS_T_FINALLY');
+}
+
+if (defined('T_YIELD') === false) {
+ define('T_YIELD', 'PHPCS_T_YIELD');
+}
+
+// Some PHP 5.6 tokens, replicated for lower versions.
+if (defined('T_ELLIPSIS') === false) {
+ define('T_ELLIPSIS', 'PHPCS_T_ELLIPSIS');
+}
+
+if (defined('T_POW') === false) {
+ define('T_POW', 'PHPCS_T_POW');
+}
+
+if (defined('T_POW_EQUAL') === false) {
+ define('T_POW_EQUAL', 'PHPCS_T_POW_EQUAL');
+}
+
+// Some PHP 7 tokens, replicated for lower versions.
+if (defined('T_SPACESHIP') === false) {
+ define('T_SPACESHIP', 'PHPCS_T_SPACESHIP');
+}
+
+if (defined('T_COALESCE') === false) {
+ define('T_COALESCE', 'PHPCS_T_COALESCE');
+}
+
+if (defined('T_COALESCE_EQUAL') === false) {
+ define('T_COALESCE_EQUAL', 'PHPCS_T_COALESCE_EQUAL');
+}
+
+if (defined('T_YIELD_FROM') === false) {
+ define('T_YIELD_FROM', 'PHPCS_T_YIELD_FROM');
+}
+
+// Tokens used for parsing doc blocks.
+define('T_DOC_COMMENT_STAR', 'PHPCS_T_DOC_COMMENT_STAR');
+define('T_DOC_COMMENT_WHITESPACE', 'PHPCS_T_DOC_COMMENT_WHITESPACE');
+define('T_DOC_COMMENT_TAG', 'PHPCS_T_DOC_COMMENT_TAG');
+define('T_DOC_COMMENT_OPEN_TAG', 'PHPCS_T_DOC_COMMENT_OPEN_TAG');
+define('T_DOC_COMMENT_CLOSE_TAG', 'PHPCS_T_DOC_COMMENT_CLOSE_TAG');
+define('T_DOC_COMMENT_STRING', 'PHPCS_T_DOC_COMMENT_STRING');
+
+final class Tokens
+{
+
+ /**
+ * The token weightings.
+ *
+ * @var array<int, int>
+ */
+ public static $weightings = array(
+ T_CLASS => 1000,
+ T_INTERFACE => 1000,
+ T_TRAIT => 1000,
+ T_NAMESPACE => 1000,
+ T_FUNCTION => 100,
+ T_CLOSURE => 100,
+
+ /*
+ Conditions.
+ */
+
+ T_WHILE => 50,
+ T_FOR => 50,
+ T_FOREACH => 50,
+ T_IF => 50,
+ T_ELSE => 50,
+ T_ELSEIF => 50,
+ T_DO => 50,
+ T_TRY => 50,
+ T_CATCH => 50,
+ T_FINALLY => 50,
+ T_SWITCH => 50,
+
+ T_SELF => 25,
+ T_PARENT => 25,
+
+ /*
+ Operators and arithmetic.
+ */
+
+ T_BITWISE_AND => 8,
+ T_BITWISE_OR => 8,
+ T_BITWISE_XOR => 8,
+
+ T_MULTIPLY => 5,
+ T_DIVIDE => 5,
+ T_PLUS => 5,
+ T_MINUS => 5,
+ T_MODULUS => 5,
+ T_POW => 5,
+ T_SPACESHIP => 5,
+ T_COALESCE => 5,
+ T_COALESCE_EQUAL => 5,
+
+ T_SL => 5,
+ T_SR => 5,
+ T_SL_EQUAL => 5,
+ T_SR_EQUAL => 5,
+
+ T_EQUAL => 5,
+ T_AND_EQUAL => 5,
+ T_CONCAT_EQUAL => 5,
+ T_DIV_EQUAL => 5,
+ T_MINUS_EQUAL => 5,
+ T_MOD_EQUAL => 5,
+ T_MUL_EQUAL => 5,
+ T_OR_EQUAL => 5,
+ T_PLUS_EQUAL => 5,
+ T_XOR_EQUAL => 5,
+
+ T_BOOLEAN_AND => 5,
+ T_BOOLEAN_OR => 5,
+
+ /*
+ Equality.
+ */
+
+ T_IS_EQUAL => 5,
+ T_IS_NOT_EQUAL => 5,
+ T_IS_IDENTICAL => 5,
+ T_IS_NOT_IDENTICAL => 5,
+ T_IS_SMALLER_OR_EQUAL => 5,
+ T_IS_GREATER_OR_EQUAL => 5,
+ );
+
+ /**
+ * Tokens that represent assignments.
+ *
+ * @var array<int, int>
+ */
+ public static $assignmentTokens = array(
+ T_EQUAL => T_EQUAL,
+ T_AND_EQUAL => T_AND_EQUAL,
+ T_OR_EQUAL => T_OR_EQUAL,
+ T_CONCAT_EQUAL => T_CONCAT_EQUAL,
+ T_DIV_EQUAL => T_DIV_EQUAL,
+ T_MINUS_EQUAL => T_MINUS_EQUAL,
+ T_POW_EQUAL => T_POW_EQUAL,
+ T_MOD_EQUAL => T_MOD_EQUAL,
+ T_MUL_EQUAL => T_MUL_EQUAL,
+ T_PLUS_EQUAL => T_PLUS_EQUAL,
+ T_XOR_EQUAL => T_XOR_EQUAL,
+ T_DOUBLE_ARROW => T_DOUBLE_ARROW,
+ T_SL_EQUAL => T_SL_EQUAL,
+ T_SR_EQUAL => T_SR_EQUAL,
+ T_COALESCE_EQUAL => T_COALESCE_EQUAL,
+ );
+
+ /**
+ * Tokens that represent equality comparisons.
+ *
+ * @var array<int, int>
+ */
+ public static $equalityTokens = array(
+ T_IS_EQUAL => T_IS_EQUAL,
+ T_IS_NOT_EQUAL => T_IS_NOT_EQUAL,
+ T_IS_IDENTICAL => T_IS_IDENTICAL,
+ T_IS_NOT_IDENTICAL => T_IS_NOT_IDENTICAL,
+ T_IS_SMALLER_OR_EQUAL => T_IS_SMALLER_OR_EQUAL,
+ T_IS_GREATER_OR_EQUAL => T_IS_GREATER_OR_EQUAL,
+ );
+
+ /**
+ * Tokens that represent comparison operator.
+ *
+ * @var array<int, int>
+ */
+ public static $comparisonTokens = array(
+ T_IS_EQUAL => T_IS_EQUAL,
+ T_IS_IDENTICAL => T_IS_IDENTICAL,
+ T_IS_NOT_EQUAL => T_IS_NOT_EQUAL,
+ T_IS_NOT_IDENTICAL => T_IS_NOT_IDENTICAL,
+ T_LESS_THAN => T_LESS_THAN,
+ T_GREATER_THAN => T_GREATER_THAN,
+ T_IS_SMALLER_OR_EQUAL => T_IS_SMALLER_OR_EQUAL,
+ T_IS_GREATER_OR_EQUAL => T_IS_GREATER_OR_EQUAL,
+ T_SPACESHIP => T_SPACESHIP,
+ T_COALESCE => T_COALESCE,
+ );
+
+ /**
+ * Tokens that represent arithmetic operators.
+ *
+ * @var array<int, int>
+ */
+ public static $arithmeticTokens = array(
+ T_PLUS => T_PLUS,
+ T_MINUS => T_MINUS,
+ T_MULTIPLY => T_MULTIPLY,
+ T_DIVIDE => T_DIVIDE,
+ T_MODULUS => T_MODULUS,
+ T_POW => T_POW,
+ );
+
+ /**
+ * Tokens that represent casting.
+ *
+ * @var array<int, int>
+ */
+ public static $castTokens = array(
+ T_INT_CAST => T_INT_CAST,
+ T_STRING_CAST => T_STRING_CAST,
+ T_DOUBLE_CAST => T_DOUBLE_CAST,
+ T_ARRAY_CAST => T_ARRAY_CAST,
+ T_BOOL_CAST => T_BOOL_CAST,
+ T_OBJECT_CAST => T_OBJECT_CAST,
+ T_UNSET_CAST => T_UNSET_CAST,
+ T_BINARY_CAST => T_BINARY_CAST,
+ );
+
+ /**
+ * Token types that open parenthesis.
+ *
+ * @var array<int, int>
+ */
+ public static $parenthesisOpeners = array(
+ T_ARRAY => T_ARRAY,
+ T_FUNCTION => T_FUNCTION,
+ T_CLOSURE => T_CLOSURE,
+ T_WHILE => T_WHILE,
+ T_FOR => T_FOR,
+ T_FOREACH => T_FOREACH,
+ T_SWITCH => T_SWITCH,
+ T_IF => T_IF,
+ T_ELSEIF => T_ELSEIF,
+ T_CATCH => T_CATCH,
+ T_DECLARE => T_DECLARE,
+ );
+
+ /**
+ * Tokens that are allowed to open scopes.
+ *
+ * @var array<int, int>
+ */
+ public static $scopeOpeners = array(
+ T_CLASS => T_CLASS,
+ T_ANON_CLASS => T_ANON_CLASS,
+ T_INTERFACE => T_INTERFACE,
+ T_TRAIT => T_TRAIT,
+ T_NAMESPACE => T_NAMESPACE,
+ T_FUNCTION => T_FUNCTION,
+ T_CLOSURE => T_CLOSURE,
+ T_IF => T_IF,
+ T_SWITCH => T_SWITCH,
+ T_CASE => T_CASE,
+ T_DECLARE => T_DECLARE,
+ T_DEFAULT => T_DEFAULT,
+ T_WHILE => T_WHILE,
+ T_ELSE => T_ELSE,
+ T_ELSEIF => T_ELSEIF,
+ T_FOR => T_FOR,
+ T_FOREACH => T_FOREACH,
+ T_DO => T_DO,
+ T_TRY => T_TRY,
+ T_CATCH => T_CATCH,
+ T_FINALLY => T_FINALLY,
+ T_PROPERTY => T_PROPERTY,
+ T_OBJECT => T_OBJECT,
+ T_USE => T_USE,
+ );
+
+ /**
+ * Tokens that represent scope modifiers.
+ *
+ * @var array<int, int>
+ */
+ public static $scopeModifiers = array(
+ T_PRIVATE => T_PRIVATE,
+ T_PUBLIC => T_PUBLIC,
+ T_PROTECTED => T_PROTECTED,
+ );
+
+ /**
+ * Tokens that can prefix a method name
+ *
+ * @var array<int, int>
+ */
+ public static $methodPrefixes = array(
+ T_PRIVATE => T_PRIVATE,
+ T_PUBLIC => T_PUBLIC,
+ T_PROTECTED => T_PROTECTED,
+ T_ABSTRACT => T_ABSTRACT,
+ T_STATIC => T_STATIC,
+ T_FINAL => T_FINAL,
+ );
+
+ /**
+ * Tokens that perform operations.
+ *
+ * @var array<int, int>
+ */
+ public static $operators = array(
+ T_MINUS => T_MINUS,
+ T_PLUS => T_PLUS,
+ T_MULTIPLY => T_MULTIPLY,
+ T_DIVIDE => T_DIVIDE,
+ T_MODULUS => T_MODULUS,
+ T_POW => T_POW,
+ T_SPACESHIP => T_SPACESHIP,
+ T_COALESCE => T_COALESCE,
+ T_BITWISE_AND => T_BITWISE_AND,
+ T_BITWISE_OR => T_BITWISE_OR,
+ T_BITWISE_XOR => T_BITWISE_XOR,
+ T_SL => T_SL,
+ T_SR => T_SR,
+ );
+
+ /**
+ * Tokens that perform boolean operations.
+ *
+ * @var array<int, int>
+ */
+ public static $booleanOperators = array(
+ T_BOOLEAN_AND => T_BOOLEAN_AND,
+ T_BOOLEAN_OR => T_BOOLEAN_OR,
+ T_LOGICAL_AND => T_LOGICAL_AND,
+ T_LOGICAL_OR => T_LOGICAL_OR,
+ T_LOGICAL_XOR => T_LOGICAL_XOR,
+ );
+
+ /**
+ * Tokens that open code blocks.
+ *
+ * @var array<int, int>
+ */
+ public static $blockOpeners = array(
+ T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
+ T_OPEN_SQUARE_BRACKET => T_OPEN_SQUARE_BRACKET,
+ T_OPEN_PARENTHESIS => T_OPEN_PARENTHESIS,
+ T_OBJECT => T_OBJECT,
+ );
+
+ /**
+ * Tokens that don't represent code.
+ *
+ * @var array<int, int>
+ */
+ public static $emptyTokens = array(
+ T_WHITESPACE => T_WHITESPACE,
+ T_COMMENT => T_COMMENT,
+ T_DOC_COMMENT => T_DOC_COMMENT,
+ T_DOC_COMMENT_STAR => T_DOC_COMMENT_STAR,
+ T_DOC_COMMENT_WHITESPACE => T_DOC_COMMENT_WHITESPACE,
+ T_DOC_COMMENT_TAG => T_DOC_COMMENT_TAG,
+ T_DOC_COMMENT_OPEN_TAG => T_DOC_COMMENT_OPEN_TAG,
+ T_DOC_COMMENT_CLOSE_TAG => T_DOC_COMMENT_CLOSE_TAG,
+ T_DOC_COMMENT_STRING => T_DOC_COMMENT_STRING,
+ );
+
+ /**
+ * Tokens that are comments.
+ *
+ * @var array<int, int>
+ */
+ public static $commentTokens = array(
+ T_COMMENT => T_COMMENT,
+ T_DOC_COMMENT => T_DOC_COMMENT,
+ T_DOC_COMMENT_STAR => T_DOC_COMMENT_STAR,
+ T_DOC_COMMENT_WHITESPACE => T_DOC_COMMENT_WHITESPACE,
+ T_DOC_COMMENT_TAG => T_DOC_COMMENT_TAG,
+ T_DOC_COMMENT_OPEN_TAG => T_DOC_COMMENT_OPEN_TAG,
+ T_DOC_COMMENT_CLOSE_TAG => T_DOC_COMMENT_CLOSE_TAG,
+ T_DOC_COMMENT_STRING => T_DOC_COMMENT_STRING,
+ );
+
+ /**
+ * Tokens that represent strings.
+ *
+ * Note that T_STRINGS are NOT represented in this list.
+ *
+ * @var array<int, int>
+ */
+ public static $stringTokens = array(
+ T_CONSTANT_ENCAPSED_STRING => T_CONSTANT_ENCAPSED_STRING,
+ T_DOUBLE_QUOTED_STRING => T_DOUBLE_QUOTED_STRING,
+ );
+
+ /**
+ * Tokens that represent text strings.
+ *
+ * @var array<int, int>
+ */
+ public static $textStringTokens = array(
+ T_CONSTANT_ENCAPSED_STRING => T_CONSTANT_ENCAPSED_STRING,
+ T_DOUBLE_QUOTED_STRING => T_DOUBLE_QUOTED_STRING,
+ T_INLINE_HTML => T_INLINE_HTML,
+ T_HEREDOC => T_HEREDOC,
+ T_NOWDOC => T_NOWDOC,
+ );
+
+ /**
+ * Tokens that represent brackets and parenthesis.
+ *
+ * @var array<int, int>
+ */
+ public static $bracketTokens = array(
+ T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
+ T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET,
+ T_OPEN_SQUARE_BRACKET => T_OPEN_SQUARE_BRACKET,
+ T_CLOSE_SQUARE_BRACKET => T_CLOSE_SQUARE_BRACKET,
+ T_OPEN_PARENTHESIS => T_OPEN_PARENTHESIS,
+ T_CLOSE_PARENTHESIS => T_CLOSE_PARENTHESIS,
+ );
+
+ /**
+ * Tokens that include files.
+ *
+ * @var array<int, int>
+ */
+ public static $includeTokens = array(
+ T_REQUIRE_ONCE => T_REQUIRE_ONCE,
+ T_REQUIRE => T_REQUIRE,
+ T_INCLUDE_ONCE => T_INCLUDE_ONCE,
+ T_INCLUDE => T_INCLUDE,
+ );
+
+ /**
+ * Tokens that make up a heredoc string.
+ *
+ * @var array<int, int>
+ */
+ public static $heredocTokens = array(
+ T_START_HEREDOC => T_START_HEREDOC,
+ T_END_HEREDOC => T_END_HEREDOC,
+ T_HEREDOC => T_HEREDOC,
+ T_START_NOWDOC => T_START_NOWDOC,
+ T_END_NOWDOC => T_END_NOWDOC,
+ T_NOWDOC => T_NOWDOC,
+ );
+
+ /**
+ * Tokens that represent the names of called functions.
+ *
+ * Mostly, these are just strings. But PHP tokenizes some language
+ * constructs and functions using their own tokens.
+ *
+ * @var array<int, int>
+ */
+ public static $functionNameTokens = array(
+ T_STRING => T_STRING,
+ T_EVAL => T_EVAL,
+ T_EXIT => T_EXIT,
+ T_INCLUDE => T_INCLUDE,
+ T_INCLUDE_ONCE => T_INCLUDE_ONCE,
+ T_REQUIRE => T_REQUIRE,
+ T_REQUIRE_ONCE => T_REQUIRE_ONCE,
+ T_ISSET => T_ISSET,
+ T_UNSET => T_UNSET,
+ T_EMPTY => T_EMPTY,
+ T_SELF => T_SELF,
+ T_STATIC => T_STATIC,
+ );
+
+ /**
+ * Tokens that are open class and object scopes.
+ *
+ * @var array<int, int>
+ */
+ public static $ooScopeTokens = array(
+ T_CLASS => T_CLASS,
+ T_ANON_CLASS => T_ANON_CLASS,
+ T_INTERFACE => T_INTERFACE,
+ T_TRAIT => T_TRAIT,
+ );
+
+
+ /**
+ * Given a token, returns the name of the token.
+ *
+ * If passed an integer, the token name is sourced from PHP's token_name()
+ * function. If passed a string, it is assumed to be a PHPCS-supplied token
+ * that begins with PHPCS_T_, so the name is sourced from the token value itself.
+ *
+ * @param int|string $token The token to get the name for.
+ *
+ * @return string
+ */
+ public static function tokenName($token)
+ {
+ if (is_string($token) === false) {
+ // PHP-supplied token name.
+ return token_name($token);
+ }
+
+ return substr($token, 6);
+
+ }//end tokenName()
+
+
+ /**
+ * Returns the highest weighted token type.
+ *
+ * Tokens are weighted by their approximate frequency of appearance in code
+ * - the less frequently they appear in the code, the higher the weighting.
+ * For example T_CLASS tokens appear very infrequently in a file, and
+ * therefore have a high weighting.
+ *
+ * Returns false if there are no weightings for any of the specified tokens.
+ *
+ * @param array<int, int> $tokens The token types to get the highest weighted
+ * type for.
+ *
+ * @return int The highest weighted token.
+ */
+ public static function getHighestWeightedToken(array $tokens)
+ {
+ $highest = -1;
+ $highestType = false;
+
+ $weights = self::$weightings;
+
+ foreach ($tokens as $token) {
+ if (isset($weights[$token]) === true) {
+ $weight = $weights[$token];
+ } else {
+ $weight = 0;
+ }
+
+ if ($weight > $highest) {
+ $highest = $weight;
+ $highestType = $token;
+ }
+ }
+
+ return $highestType;
+
+ }//end getHighestWeightedToken()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * A test class for running all PHP_CodeSniffer unit tests.
+ *
+ * @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\Tests;
+
+use PHP_CodeSniffer\Tests\TestSuite;
+use PHPUnit\TextUI\TestRunner;
+
+if (is_file(__DIR__.'/../autoload.php') === true) {
+ include_once 'Core/AllTests.php';
+ include_once 'Standards/AllSniffs.php';
+} else {
+ include_once 'CodeSniffer/Core/AllTests.php';
+ include_once 'CodeSniffer/Standards/AllSniffs.php';
+}
+
+require_once 'TestSuite.php';
+
+class PHP_CodeSniffer_AllTests
+{
+
+
+ /**
+ * Prepare the test runner.
+ *
+ * @return void
+ */
+ public static function main()
+ {
+ TestRunner::run(self::suite());
+
+ }//end main()
+
+
+ /**
+ * Add all PHP_CodeSniffer test suites into a single test suite.
+ *
+ * @return \PHPUnit\Framework\TestSuite
+ */
+ public static function suite()
+ {
+ $GLOBALS['PHP_CODESNIFFER_STANDARD_DIRS'] = array();
+ $GLOBALS['PHP_CODESNIFFER_TEST_DIRS'] = array();
+
+ // Use a special PHP_CodeSniffer test suite so that we can
+ // unset our autoload function after the run.
+ $suite = new TestSuite('PHP CodeSniffer');
+
+ $suite->addTest(Core\AllTests::suite());
+ $suite->addTest(Standards\AllSniffs::suite());
+
+ return $suite;
+
+ }//end suite()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * A test class for testing the core.
+ *
+ * @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\Tests\Core;
+
+use PHPUnit\TextUI\TestRunner;
+use PHPUnit\Framework\TestSuite;
+
+require_once 'IsCamelCapsTest.php';
+require_once 'ErrorSuppressionTest.php';
+require_once 'File/GetMethodParametersTest.php';
+require_once 'File/FindExtendedClassNameTest.php';
+require_once 'File/FindImplementedInterfaceNamesTest.php';
+require_once 'File/IsReferenceTest.php';
+
+class AllTests
+{
+
+
+ /**
+ * Prepare the test runner.
+ *
+ * @return void
+ */
+ public static function main()
+ {
+ TestRunner::run(self::suite());
+
+ }//end main()
+
+
+ /**
+ * Add all core unit tests into a test suite.
+ *
+ * @return \PHPUnit\Framework\TestSuite
+ */
+ public static function suite()
+ {
+ $suite = new TestSuite('PHP CodeSniffer Core');
+ $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\IsCamelCapsTest');
+ $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\ErrorSuppressionTest');
+ $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\File\GetMethodParametersTest');
+ $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\File\FindExtendedClassNameTest');
+ $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\File\FindImplementedInterfaceNamesTest');
+ $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\File\IsReferenceTest');
+ return $suite;
+
+ }//end suite()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tests for PHP_CodeSniffer error suppression tags.
+ *
+ * @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\Tests\Core;
+
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Ruleset;
+use PHP_CodeSniffer\Files\DummyFile;
+use PHPUnit\Framework\TestCase;
+
+class ErrorSuppressionTest extends TestCase
+{
+
+
+ /**
+ * Test suppressing a single error.
+ *
+ * @return void
+ */
+ public function testSuppressError()
+ {
+ $config = new Config();
+ $config->standards = array('Generic');
+ $config->sniffs = array('Generic.PHP.LowerCaseConstant');
+
+ $ruleset = new Ruleset($config);
+
+ // Process without suppression.
+ $content = '<?php '.PHP_EOL.'$var = FALSE;';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(1, $numErrors);
+ $this->assertEquals(1, count($errors));
+
+ // Process with inline comment suppression.
+ $content = '<?php '.PHP_EOL.'// @codingStandardsIgnoreStart'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'// @codingStandardsIgnoreEnd';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(0, $numErrors);
+ $this->assertEquals(0, count($errors));
+
+ // Process with block comment suppression.
+ $content = '<?php '.PHP_EOL.'/* @codingStandardsIgnoreStart */'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'/* @codingStandardsIgnoreEnd */';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(0, $numErrors);
+ $this->assertEquals(0, count($errors));
+
+ // Process with a docblock suppression.
+ $content = '<?php '.PHP_EOL.'/** @codingStandardsIgnoreStart */'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'/** @codingStandardsIgnoreEnd */';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(0, $numErrors);
+ $this->assertEquals(0, count($errors));
+
+ }//end testSuppressError()
+
+
+ /**
+ * Test suppressing 1 out of 2 errors.
+ *
+ * @return void
+ */
+ public function testSuppressSomeErrors()
+ {
+ $config = new Config();
+ $config->standards = array('Generic');
+ $config->sniffs = array('Generic.PHP.LowerCaseConstant');
+
+ $ruleset = new Ruleset($config);
+
+ // Process without suppression.
+ $content = '<?php '.PHP_EOL.'$var = FALSE;'.PHP_EOL.'$var = TRUE;';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(2, $numErrors);
+ $this->assertEquals(2, count($errors));
+
+ // Process with suppression.
+ $content = '<?php '.PHP_EOL.'// @codingStandardsIgnoreStart'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'// @codingStandardsIgnoreEnd'.PHP_EOL.'$var = TRUE;';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(1, $numErrors);
+ $this->assertEquals(1, count($errors));
+
+ // Process with a PHPDoc block suppression.
+ $content = '<?php '.PHP_EOL.'/** @codingStandardsIgnoreStart */'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'/** @codingStandardsIgnoreEnd */'.PHP_EOL.'$var = TRUE;';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(1, $numErrors);
+ $this->assertEquals(1, count($errors));
+
+ }//end testSuppressSomeErrors()
+
+
+ /**
+ * Test suppressing a single warning.
+ *
+ * @return void
+ */
+ public function testSuppressWarning()
+ {
+ $config = new Config();
+ $config->standards = array('Generic');
+ $config->sniffs = array('Generic.Commenting.Todo');
+
+ $ruleset = new Ruleset($config);
+
+ // Process without suppression.
+ $content = '<?php '.PHP_EOL.'//TODO: write some code';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $warnings = $file->getWarnings();
+ $numWarnings = $file->getWarningCount();
+ $this->assertEquals(1, $numWarnings);
+ $this->assertEquals(1, count($warnings));
+
+ // Process with suppression.
+ $content = '<?php '.PHP_EOL.'// @codingStandardsIgnoreStart'.PHP_EOL.'//TODO: write some code'.PHP_EOL.'// @codingStandardsIgnoreEnd';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $warnings = $file->getWarnings();
+ $numWarnings = $file->getWarningCount();
+ $this->assertEquals(0, $numWarnings);
+ $this->assertEquals(0, count($warnings));
+
+ // Process with a docblock suppression.
+ $content = '<?php '.PHP_EOL.'/** @codingStandardsIgnoreStart */'.PHP_EOL.'//TODO: write some code'.PHP_EOL.'/** @codingStandardsIgnoreEnd */';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $warnings = $file->getWarnings();
+ $numWarnings = $file->getWarningCount();
+ $this->assertEquals(0, $numWarnings);
+ $this->assertEquals(0, count($warnings));
+
+ }//end testSuppressWarning()
+
+
+ /**
+ * Test suppressing a single error using a single line ignore.
+ *
+ * @return void
+ */
+ public function testSuppressLine()
+ {
+ $config = new Config();
+ $config->standards = array('Generic');
+ $config->sniffs = array('Generic.PHP.LowerCaseConstant');
+
+ $ruleset = new Ruleset($config);
+
+ // Process without suppression.
+ $content = '<?php '.PHP_EOL.'$var = FALSE;'.PHP_EOL.'$var = FALSE;';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(2, $numErrors);
+ $this->assertEquals(2, count($errors));
+
+ // Process with suppression.
+ $content = '<?php '.PHP_EOL.'// @codingStandardsIgnoreLine'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'$var = FALSE;';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(1, $numErrors);
+ $this->assertEquals(1, count($errors));
+
+ }//end testSuppressLine()
+
+
+ /**
+ * Test that using a single line ignore does not interfere with other suppressions.
+ *
+ * @return void
+ */
+ public function testNestedSuppressLine()
+ {
+ $config = new Config();
+ $config->standards = array('Generic');
+ $config->sniffs = array('Generic.PHP.LowerCaseConstant');
+
+ $ruleset = new Ruleset($config);
+
+ // Process with codingStandardsIgnore[Start|End] suppression and no single line suppression.
+ $content = '<?php '.PHP_EOL.'// @codingStandardsIgnoreStart'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'$var = TRUE;'.PHP_EOL.'// @codingStandardsIgnoreEnd';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(0, $numErrors);
+ $this->assertEquals(0, count($errors));
+
+ // Process with codingStandardsIgnoreLine suppression
+ // nested within codingStandardsIgnore[Start|End] suppression.
+ $content = '<?php '.PHP_EOL.'// @codingStandardsIgnoreStart'.PHP_EOL.'// @codingStandardsIgnoreLine'.PHP_EOL.'$var = FALSE;'.PHP_EOL.'$var = TRUE;'.PHP_EOL.'// @codingStandardsIgnoreEnd';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(0, $numErrors);
+ $this->assertEquals(0, count($errors));
+
+ }//end testNestedSuppressLine()
+
+
+ /**
+ * Test suppressing a scope opener.
+ *
+ * @return void
+ */
+ public function testSuppressScope()
+ {
+ return;
+ $phpcs = new PHP_CodeSniffer();
+ $phpcs->initStandard('PEAR', array('PEAR.NamingConventions.ValidVariableName'));
+
+ // Process without suppression.
+ $content = '<?php '.PHP_EOL.'class MyClass() {'.PHP_EOL.'function myFunction() {'.PHP_EOL.'$this->foo();'.PHP_EOL.'}'.PHP_EOL.'}';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(0, $numErrors);
+ $this->assertEquals(0, count($errors));
+
+ // Process with suppression.
+ $content = '<?php '.PHP_EOL.'class MyClass() {'.PHP_EOL.'//@codingStandardsIgnoreStart'.PHP_EOL.'function myFunction() {'.PHP_EOL.'//@codingStandardsIgnoreEnd'.PHP_EOL.'$this->foo();'.PHP_EOL.'}'.PHP_EOL.'}';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(0, $numErrors);
+ $this->assertEquals(0, count($errors));
+
+ // Process with a docblock suppression.
+ $content = '<?php '.PHP_EOL.'class MyClass() {'.PHP_EOL.'/** @codingStandardsIgnoreStart */'.PHP_EOL.'function myFunction() {'.PHP_EOL.'/** @codingStandardsIgnoreEnd */'.PHP_EOL.'$this->foo();'.PHP_EOL.'}'.PHP_EOL.'}';
+ $file = $phpcs->processFile('suppressionTest.php', $content);
+
+ $errors = $file->getErrors();
+ $numErrors = $file->getErrorCount();
+ $this->assertEquals(0, $numErrors);
+ $this->assertEquals(0, count($errors));
+
+ }//end testSuppressScope()
+
+
+ /**
+ * Test suppressing a whole file.
+ *
+ * @return void
+ */
+ public function testSuppressFile()
+ {
+ $config = new Config();
+ $config->standards = array('Generic');
+ $config->sniffs = array('Generic.Commenting.Todo');
+
+ $ruleset = new Ruleset($config);
+
+ // Process without suppression.
+ $content = '<?php '.PHP_EOL.'//TODO: write some code';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $warnings = $file->getWarnings();
+ $numWarnings = $file->getWarningCount();
+ $this->assertEquals(1, $numWarnings);
+ $this->assertEquals(1, count($warnings));
+
+ // Process with suppression.
+ $content = '<?php '.PHP_EOL.'// @codingStandardsIgnoreFile'.PHP_EOL.'//TODO: write some code';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $warnings = $file->getWarnings();
+ $numWarnings = $file->getWarningCount();
+ $this->assertEquals(0, $numWarnings);
+ $this->assertEquals(0, count($warnings));
+
+ // Process with a block comment suppression.
+ $content = '<?php '.PHP_EOL.'/* @codingStandardsIgnoreFile */'.PHP_EOL.'//TODO: write some code';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $warnings = $file->getWarnings();
+ $numWarnings = $file->getWarningCount();
+ $this->assertEquals(0, $numWarnings);
+ $this->assertEquals(0, count($warnings));
+
+ // Process with docblock suppression.
+ $content = '<?php '.PHP_EOL.'/** @codingStandardsIgnoreFile */'.PHP_EOL.'//TODO: write some code';
+ $file = new DummyFile($content, $ruleset, $config);
+ $file->process();
+
+ $warnings = $file->getWarnings();
+ $numWarnings = $file->getWarningCount();
+ $this->assertEquals(0, $numWarnings);
+ $this->assertEquals(0, count($warnings));
+
+ }//end testSuppressFile()
+
+
+}//end class
--- /dev/null
+<?php
+/* @codingStandardsIgnoreFile */
+
+namespace PHP_CodeSniffer\Tests\Core\File;
+
+class testFECNClass {}
+
+/* testExtendedClass */
+class testFECNExtendedClass extends testFECNClass {}
+
+/* testNamespacedClass */
+class testFECNNamespacedClass extends \PHP_CodeSniffer\Tests\Core\File\testFECNClass {}
+
+/* testNonExtendedClass */
+class testFECNNonExtendedClass {}
+
+/* testInterface */
+interface testFECNInterface {}
+
+/* testInterfaceThatExtendsInterface */
+interface testInterfaceThatExtendsInterface extends testFECNInterface{}
+
+/* testInterfaceThatExtendsFQCNInterface */
+interface testInterfaceThatExtendsFQCNInterface extends \PHP_CodeSniffer\Tests\Core\File\testFECNInterface{}
--- /dev/null
+<?php
+/**
+ * Tests for the \PHP_CodeSniffer\Files\File:findExtendedClassName method.
+ *
+ * @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\Tests\Core\File;
+
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Ruleset;
+use PHP_CodeSniffer\Files\DummyFile;
+use PHPUnit\Framework\TestCase;
+
+class FindExtendedClassNameTest extends TestCase
+{
+
+ /**
+ * The PHP_CodeSniffer_File object containing parsed contents of the test case file.
+ *
+ * @var \PHP_CodeSniffer\Files\File
+ */
+ private $phpcsFile;
+
+
+ /**
+ * Initialize & tokenize \PHP_CodeSniffer\Files\File with code from the test case file.
+ *
+ * Methods used for these tests can be found in a test case file in the same
+ * directory and with the same name, using the .inc extension.
+ *
+ * @return void
+ */
+ public function setUp()
+ {
+ $config = new Config();
+ $config->standards = array('Generic');
+
+ $ruleset = new Ruleset($config);
+
+ $pathToTestFile = dirname(__FILE__).'/'.basename(__FILE__, '.php').'.inc';
+ $this->phpcsFile = new DummyFile(file_get_contents($pathToTestFile), $ruleset, $config);
+ $this->phpcsFile->process();
+
+ }//end setUp()
+
+
+ /**
+ * Clean up after finished test.
+ *
+ * @return void
+ */
+ public function tearDown()
+ {
+ unset($this->phpcsFile);
+
+ }//end tearDown()
+
+
+ /**
+ * Test a class that extends another.
+ *
+ * @return void
+ */
+ public function testExtendedClass()
+ {
+ $start = ($this->phpcsFile->numTokens - 1);
+ $class = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testExtendedClass */'
+ );
+
+ $found = $this->phpcsFile->findExtendedClassName(($class + 2));
+ $this->assertSame('testFECNClass', $found);
+
+ }//end testExtendedClass()
+
+
+ /**
+ * Test a class that extends another, using namespaces.
+ *
+ * @return void
+ */
+ public function testNamespacedClass()
+ {
+ $start = ($this->phpcsFile->numTokens - 1);
+ $class = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testNamespacedClass */'
+ );
+
+ $found = $this->phpcsFile->findExtendedClassName(($class + 2));
+ $this->assertSame('\PHP_CodeSniffer\Tests\Core\File\testFECNClass', $found);
+
+ }//end testNamespacedClass()
+
+
+ /**
+ * Test a class that doesn't extend another.
+ *
+ * @return void
+ */
+ public function testNonExtendedClass()
+ {
+ $start = ($this->phpcsFile->numTokens - 1);
+ $class = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testNonExtendedClass */'
+ );
+
+ $found = $this->phpcsFile->findExtendedClassName(($class + 2));
+ $this->assertFalse($found);
+
+ }//end testNonExtendedClass()
+
+
+ /**
+ * Test an interface.
+ *
+ * @return void
+ */
+ public function testInterface()
+ {
+ $start = ($this->phpcsFile->numTokens - 1);
+ $class = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testInterface */'
+ );
+
+ $found = $this->phpcsFile->findExtendedClassName(($class + 2));
+ $this->assertFalse($found);
+
+ }//end testInterface()
+
+
+ /**
+ * Test an interface that extends another.
+ *
+ * @return void
+ */
+ public function testExtendedInterface()
+ {
+ $start = ($this->phpcsFile->numTokens - 1);
+ $class = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testInterfaceThatExtendsInterface */'
+ );
+
+ $found = $this->phpcsFile->findExtendedClassName(($class + 2));
+ $this->assertSame('testFECNInterface', $found);
+
+ }//end testExtendedInterface()
+
+
+ /**
+ * Test an interface that extends another, using namespaces.
+ *
+ * @return void
+ */
+ public function testExtendedNamespacedInterface()
+ {
+ $start = ($this->phpcsFile->numTokens - 1);
+ $class = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testInterfaceThatExtendsFQCNInterface */'
+ );
+
+ $found = $this->phpcsFile->findExtendedClassName(($class + 2));
+ $this->assertSame('\PHP_CodeSniffer\Tests\Core\File\testFECNInterface', $found);
+
+ }//end testExtendedNamespacedInterface()
+
+
+}//end class
--- /dev/null
+<?php
+/* @codingStandardsIgnoreFile */
+
+namespace PHP_CodeSniffer\Tests\Core\File;
+
+\PHP_CodeSniffer\Tests\Core\File\testFECNClass
+interface testFIINInterface2 {}
+
+/* testInterface */
+interface testFIINInterface {}
+
+/* testImplementedClass */
+class testFIINImplementedClass implements testFIINInterface {}
+
+/* testMultiImplementedClass */
+class testFIINMultiImplementedClass implements testFIINInterface, testFIINInterface2 {}
+
+/* testNamespacedClass */
+class testFIINNamespacedClass implements \PHP_CodeSniffer\Tests\Core\File\testFIINInterface {}
+
+/* testNonImplementedClass */
+class testFIINNonImplementedClass {}
--- /dev/null
+<?php
+/**
+ * Tests for the \PHP_CodeSniffer\Files\File:findImplementedInterfaceNames method.
+ *
+ * @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\Tests\Core\File;
+
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Ruleset;
+use PHP_CodeSniffer\Files\DummyFile;
+use PHPUnit\Framework\TestCase;
+
+class FindImplementedInterfaceNamesTest extends TestCase
+{
+
+ /**
+ * The \PHP_CodeSniffer\Files\File object containing parsed contents of the test case file.
+ *
+ * @var \PHP_CodeSniffer\Files\File
+ */
+ private $phpcsFile;
+
+
+ /**
+ * Initialize & tokenize \PHP_CodeSniffer\Files\File with code from the test case file.
+ *
+ * Methods used for these tests can be found in a test case file in the same
+ * directory and with the same name, using the .inc extension.
+ *
+ * @return void
+ */
+ public function setUp()
+ {
+ $config = new Config();
+ $config->standards = array('Generic');
+
+ $ruleset = new Ruleset($config);
+
+ $pathToTestFile = dirname(__FILE__).'/'.basename(__FILE__, '.php').'.inc';
+ $this->phpcsFile = new DummyFile(file_get_contents($pathToTestFile), $ruleset, $config);
+ $this->phpcsFile->process();
+
+ }//end setUp()
+
+
+ /**
+ * Clean up after finished test.
+ *
+ * @return void
+ */
+ public function tearDown()
+ {
+ unset($this->phpcsFile);
+
+ }//end tearDown()
+
+
+ /**
+ * Test a class that implements a single interface.
+ *
+ * @return void
+ */
+ public function testImplementedClass()
+ {
+ $start = ($this->phpcsFile->numTokens - 1);
+ $class = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testImplementedClass */'
+ );
+
+ $found = $this->phpcsFile->findImplementedInterfaceNames(($class + 2));
+ $this->assertSame(array('testFIINInterface'), $found);
+
+ }//end testImplementedClass()
+
+
+ /**
+ * Test a class that implements multiple interfaces.
+ *
+ * @return void
+ */
+ public function testMultiImplementedClass()
+ {
+ $start = ($this->phpcsFile->numTokens - 1);
+ $class = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testMultiImplementedClass */'
+ );
+
+ $found = $this->phpcsFile->findImplementedInterfaceNames(($class + 2));
+ $this->assertSame(array('testFIINInterface', 'testFIINInterface2'), $found);
+
+ }//end testMultiImplementedClass()
+
+
+ /**
+ * Test a class that implements an interface, using namespaces.
+ *
+ * @return void
+ */
+ public function testNamespacedClass()
+ {
+ $start = ($this->phpcsFile->numTokens - 1);
+ $class = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testNamespacedClass */'
+ );
+
+ $found = $this->phpcsFile->findImplementedInterfaceNames(($class + 2));
+ $this->assertSame(array('\PHP_CodeSniffer\Tests\Core\File\testFIINInterface'), $found);
+
+ }//end testNamespacedClass()
+
+
+ /**
+ * Test a class that doesn't implement an interface.
+ *
+ * @return void
+ */
+ public function testNonImplementedClass()
+ {
+ $start = ($this->phpcsFile->numTokens - 1);
+ $class = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testNonImplementedClass */'
+ );
+
+ $found = $this->phpcsFile->findImplementedInterfaceNames(($class + 2));
+ $this->assertFalse($found);
+
+ }//end testNonImplementedClass()
+
+
+ /**
+ * Test an interface.
+ *
+ * @return void
+ */
+ public function testInterface()
+ {
+ $start = ($this->phpcsFile->numTokens - 1);
+ $class = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testInterface */'
+ );
+
+ $found = $this->phpcsFile->findImplementedInterfaceNames(($class + 2));
+ $this->assertFalse($found);
+
+ }//end testInterface()
+
+
+}//end class
--- /dev/null
+<?php
+/* @codingStandardsIgnoreFile */
+
+/* testPassByReference */
+function passByReference(&$var) {}
+
+/* testArrayHint */
+function arrayHint(array $var) {}
+
+/* testVariable */
+function variable($var) {}
+
+/* testSingleDefaultValue */
+function defaultValue($var1=self::CONSTANT) {}
+
+/* testDefaultValues */
+function defaultValues($var1=1, $var2='value') {}
+
+/* testTypeHint */
+function typeHint(foo $var1, bar $var2) {}
+
+class MyClass {
+/* testSelfTypeHint */ function typeSelfHint(self $var) {}
+}
+
+/* testNullableTypeHint */
+function nullableTypeHint(?int $var1, ?\bar $var2) {}
+
+/* testBitwiseAndConstantExpressionDefaultValue */
+function myFunction($a = 10 & 20) {}
--- /dev/null
+<?php
+/**
+ * Tests for the \PHP_CodeSniffer\Files\File:getMethodParameters method.
+ *
+ * @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\Tests\Core\File;
+
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Ruleset;
+use PHP_CodeSniffer\Files\DummyFile;
+use PHPUnit\Framework\TestCase;
+
+class GetMethodParametersTest extends TestCase
+{
+
+ /**
+ * The PHP_CodeSniffer_File object containing parsed contents of the test case file.
+ *
+ * @var \PHP_CodeSniffer\Files\File
+ */
+ private $phpcsFile;
+
+
+ /**
+ * Initialize & tokenize PHP_CodeSniffer_File with code from the test case file.
+ *
+ * Methods used for these tests can be found in a test case file in the same
+ * directory and with the same name, using the .inc extension.
+ *
+ * @return void
+ */
+ public function setUp()
+ {
+ $config = new Config();
+ $config->standards = array('Generic');
+
+ $ruleset = new Ruleset($config);
+
+ $pathToTestFile = dirname(__FILE__).'/'.basename(__FILE__, '.php').'.inc';
+ $this->phpcsFile = new DummyFile(file_get_contents($pathToTestFile), $ruleset, $config);
+ $this->phpcsFile->process();
+
+ }//end setUp()
+
+
+ /**
+ * Clean up after finished test.
+ *
+ * @return void
+ */
+ public function tearDown()
+ {
+ unset($this->phpcsFile);
+
+ }//end tearDown()
+
+
+ /**
+ * Verify pass-by-reference parsing.
+ *
+ * @return void
+ */
+ public function testPassByReference()
+ {
+ $expected = array();
+ $expected[0] = array(
+ 'name' => '$var',
+ 'content' => '&$var',
+ 'pass_by_reference' => true,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ );
+
+ $start = ($this->phpcsFile->numTokens - 1);
+ $function = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testPassByReference */'
+ );
+
+ $found = $this->phpcsFile->getMethodParameters(($function + 2));
+ unset($found[0]['token']);
+ $this->assertSame($expected, $found);
+
+ }//end testPassByReference()
+
+
+ /**
+ * Verify array hint parsing.
+ *
+ * @return void
+ */
+ public function testArrayHint()
+ {
+ $expected = array();
+ $expected[0] = array(
+ 'name' => '$var',
+ 'content' => 'array $var',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => 'array',
+ 'nullable_type' => false,
+ );
+
+ $start = ($this->phpcsFile->numTokens - 1);
+ $function = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testArrayHint */'
+ );
+
+ $found = $this->phpcsFile->getMethodParameters(($function + 2));
+ unset($found[0]['token']);
+ $this->assertSame($expected, $found);
+
+ }//end testArrayHint()
+
+
+ /**
+ * Verify type hint parsing.
+ *
+ * @return void
+ */
+ public function testTypeHint()
+ {
+ $expected = array();
+ $expected[0] = array(
+ 'name' => '$var1',
+ 'content' => 'foo $var1',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => 'foo',
+ 'nullable_type' => false,
+ );
+
+ $expected[1] = array(
+ 'name' => '$var2',
+ 'content' => 'bar $var2',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => 'bar',
+ 'nullable_type' => false,
+ );
+
+ $start = ($this->phpcsFile->numTokens - 1);
+ $function = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testTypeHint */'
+ );
+
+ $found = $this->phpcsFile->getMethodParameters(($function + 2));
+ unset($found[0]['token']);
+ unset($found[1]['token']);
+ $this->assertSame($expected, $found);
+
+ }//end testTypeHint()
+
+
+ /**
+ * Verify self type hint parsing.
+ *
+ * @return void
+ */
+ public function testSelfTypeHint()
+ {
+ $expected = array();
+ $expected[0] = array(
+ 'name' => '$var',
+ 'content' => 'self $var',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => 'self',
+ 'nullable_type' => false,
+ );
+
+ $start = ($this->phpcsFile->numTokens - 1);
+ $function = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testSelfTypeHint */'
+ );
+
+ $found = $this->phpcsFile->getMethodParameters(($function + 2));
+ unset($found[0]['token']);
+ $this->assertSame($expected, $found);
+
+ }//end testSelfTypeHint()
+
+
+ /**
+ * Verify nullable type hint parsing.
+ *
+ * @return void
+ */
+ public function testNullableTypeHint()
+ {
+ $expected = array();
+ $expected[0] = array(
+ 'name' => '$var1',
+ 'content' => '?int $var1',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '?int',
+ 'nullable_type' => true,
+ );
+
+ $expected[1] = array(
+ 'name' => '$var2',
+ 'content' => '?\bar $var2',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '?\bar',
+ 'nullable_type' => true,
+ );
+
+ $start = ($this->phpcsFile->numTokens - 1);
+ $function = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testNullableTypeHint */'
+ );
+
+ $found = $this->phpcsFile->getMethodParameters(($function + 2));
+ unset($found[0]['token']);
+ unset($found[1]['token']);
+ $this->assertSame($expected, $found);
+
+ }//end testNullableTypeHint()
+
+
+ /**
+ * Verify variable.
+ *
+ * @return void
+ */
+ public function testVariable()
+ {
+ $expected = array();
+ $expected[0] = array(
+ 'name' => '$var',
+ 'content' => '$var',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ );
+
+ $start = ($this->phpcsFile->numTokens - 1);
+ $function = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testVariable */'
+ );
+
+ $found = $this->phpcsFile->getMethodParameters(($function + 2));
+ unset($found[0]['token']);
+ $this->assertSame($expected, $found);
+
+ }//end testVariable()
+
+
+ /**
+ * Verify default value parsing with a single function param.
+ *
+ * @return void
+ */
+ public function testSingleDefaultValue()
+ {
+ $expected = array();
+ $expected[0] = array(
+ 'name' => '$var1',
+ 'content' => '$var1=self::CONSTANT',
+ 'default' => 'self::CONSTANT',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ );
+
+ $start = ($this->phpcsFile->numTokens - 1);
+ $function = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testSingleDefaultValue */'
+ );
+
+ $found = $this->phpcsFile->getMethodParameters(($function + 2));
+ unset($found[0]['token']);
+ $this->assertSame($expected, $found);
+
+ }//end testSingleDefaultValue()
+
+
+ /**
+ * Verify default value parsing.
+ *
+ * @return void
+ */
+ public function testDefaultValues()
+ {
+ $expected = array();
+ $expected[0] = array(
+ 'name' => '$var1',
+ 'content' => '$var1=1',
+ 'default' => '1',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ );
+ $expected[1] = array(
+ 'name' => '$var2',
+ 'content' => "\$var2='value'",
+ 'default' => "'value'",
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ );
+
+ $start = ($this->phpcsFile->numTokens - 1);
+ $function = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testDefaultValues */'
+ );
+
+ $found = $this->phpcsFile->getMethodParameters(($function + 2));
+ unset($found[0]['token']);
+ unset($found[1]['token']);
+ $this->assertSame($expected, $found);
+
+ }//end testDefaultValues()
+
+
+ /**
+ * Verify "bitwise and" in default value !== pass-by-reference.
+ *
+ * @return void
+ */
+ public function testBitwiseAndConstantExpressionDefaultValue()
+ {
+ $expected = array();
+ $expected[0] = array(
+ 'name' => '$a',
+ 'content' => '$a = 10 & 20',
+ 'default' => '10 & 20',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ );
+
+ $start = ($this->phpcsFile->numTokens - 1);
+ $function = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ '/* testBitwiseAndConstantExpressionDefaultValue */'
+ );
+
+ $found = $this->phpcsFile->getMethodParameters(($function + 2));
+ unset($found[0]['token']);
+ $this->assertSame($expected, $found);
+
+ }//end testBitwiseAndConstantExpressionDefaultValue()
+
+
+}//end class
--- /dev/null
+<?php
+/* @codingStandardsIgnoreFile */
+
+namespace PHP_CodeSniffer\Tests\Core\File;
+
+/* bitwiseAndA */
+error_reporting( E_NOTICE & E_STRICT );
+
+/* bitwiseAndB */
+$a = [ $something & $somethingElse ];
+
+/* bitwiseAndC */
+$a = [ $first, $something & self::$somethingElse ];
+
+/* bitwiseAndD */
+$a = array $first, $something & $somethingElse );
+
+/* bitwiseAndE */
+$a = [ 'a' => $first, 'b' => $something & $somethingElse ];
+
+/* bitwiseAndF */
+$a = array( 'a' => $first, 'b' => $something & \MyClass::$somethingElse );
+
+/* bitwiseAndG */
+$a = $something & $somethingElse;
+
+/* bitwiseAndH */
+function myFunction($a = 10 & 20) {}
+
+/* bitwiseAndI */
+$closure = function ($a = MY_CONSTANT & parent::OTHER_CONSTANT) {};
+
+/* functionReturnByReference */
+function &myFunction() {}
+
+/* functionPassByReferenceA */
+function myFunction( &$a ) {}
+
+/* functionPassByReferenceB */
+function myFunction( $a, &$b ) {}
+
+/* functionPassByReferenceC */
+$closure = function ( &$a ) {};
+
+/* functionPassByReferenceD */
+$closure = function ( $a, &$b ) {};
+
+/* functionPassByReferenceE */
+function myFunction(array &$one) {}
+
+/* functionPassByReferenceF */
+$closure = function (\MyClass &$one) {};
+
+/* functionPassByReferenceG */
+$closure = function myFunc($param, &...$moreParams) {};
+
+/* foreachValueByReference */
+foreach( $array as $key => &$value ) {}
+
+/* foreachKeyByReference */
+foreach( $array as &$key => $value ) {}
+
+/* arrayValueByReferenceA */
+$a = [ 'a' => &$something ];
+
+/* arrayValueByReferenceB */
+$a = [ 'a' => $something, 'b' => &$somethingElse ];
+
+/* arrayValueByReferenceC */
+$a = [ &$something ];
+
+/* arrayValueByReferenceD */
+$a = [ $something, &$somethingElse ];
+
+/* arrayValueByReferenceE */
+$a = array( 'a' => &$something );
+
+/* arrayValueByReferenceF */
+$a = array( 'a' => $something, 'b' => &$somethingElse );
+
+/* arrayValueByReferenceG */
+$a = array( &$something );
+
+/* arrayValueByReferenceH */
+$a = array( $something, &$somethingElse );
+
+/* assignByReferenceA */
+$b = &$something;
+
+/* assignByReferenceB */
+$b =& $something;
+
+/* assignByReferenceC */
+$b .= &$something;
+
+/* assignByReferenceD */
+$myValue = &$obj->getValue();
+
+/* assignByReferenceE */
+$collection = &collector();
+
+/* passByReferenceA */
+functionCall(&$something, $somethingElse);
+
+/* passByReferenceB */
+functionCall($something, &$somethingElse);
+
+/* passByReferenceC */
+functionCall($something, &$this->somethingElse);
+
+/* passByReferenceD */
+functionCall($something, &self::$somethingElse);
+
+/* passByReferenceE */
+functionCall($something, &parent::$somethingElse);
+
+/* passByReferenceF */
+functionCall($something, &static::$somethingElse);
+
+/* passByReferenceG */
+functionCall($something, &SomeClass::$somethingElse);
+
+/* passByReferenceH */
+functionCall(&\SomeClass::$somethingElse);
+
+/* passByReferenceI */
+functionCall($something, &\SomeNS\SomeClass::$somethingElse);
+
+/* passByReferenceJ */
+functionCall($something, &namespace\SomeClass::$somethingElse);
+
+/* newByReferenceA */
+$foobar2 = &new Foobar();
+
+/* newByReferenceB */
+functionCall( $something , &new Foobar() );
+
+/* useByReference */
+$closure = function() use (&$var){};
--- /dev/null
+<?php
+/**
+ * Tests for the \PHP_CodeSniffer\Files\File:isReference method.
+ *
+ * @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\Tests\Core\File;
+
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Ruleset;
+use PHP_CodeSniffer\Files\DummyFile;
+use PHPUnit\Framework\TestCase;
+
+class IsReferenceTest extends TestCase
+{
+
+ /**
+ * The PHP_CodeSniffer_File object containing parsed contents of the test case file.
+ *
+ * @var \PHP_CodeSniffer\Files\File
+ */
+ private $phpcsFile;
+
+
+ /**
+ * Initialize & tokenize \PHP_CodeSniffer\Files\File with code from the test case file.
+ *
+ * Methods used for these tests can be found in a test case file in the same
+ * directory and with the same name, using the .inc extension.
+ *
+ * @return void
+ */
+ public function setUp()
+ {
+ $config = new Config();
+ $config->standards = array('Generic');
+
+ $ruleset = new Ruleset($config);
+
+ $pathToTestFile = dirname(__FILE__).'/'.basename(__FILE__, '.php').'.inc';
+ $this->phpcsFile = new DummyFile(file_get_contents($pathToTestFile), $ruleset, $config);
+ $this->phpcsFile->process();
+
+ }//end setUp()
+
+
+ /**
+ * Clean up after finished test.
+ *
+ * @return void
+ */
+ public function tearDown()
+ {
+ unset($this->phpcsFile);
+
+ }//end tearDown()
+
+
+ /**
+ * Test a class that extends another.
+ *
+ * @param string $identifier Comment which preceeds the test case.
+ * @param bool $expected Expected function output.
+ *
+ * @dataProvider dataIsReference
+ *
+ * @return void
+ */
+ public function testIsReference($identifier, $expected)
+ {
+ $start = ($this->phpcsFile->numTokens - 1);
+ $delim = $this->phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ $identifier
+ );
+ $bitwiseAnd = $this->phpcsFile->findNext(T_BITWISE_AND, ($delim + 1));
+
+ $result = $this->phpcsFile->isReference($bitwiseAnd);
+ $this->assertSame($expected, $result);
+
+ }//end testIsReference()
+
+
+ /**
+ * Data provider for the IsReference test.
+ *
+ * @see testIsReference()
+ *
+ * @return array
+ */
+ public function dataIsReference()
+ {
+ return array(
+ array(
+ '/* bitwiseAndA */',
+ false,
+ ),
+ array(
+ '/* bitwiseAndB */',
+ false,
+ ),
+ array(
+ '/* bitwiseAndC */',
+ false,
+ ),
+ array(
+ '/* bitwiseAndD */',
+ false,
+ ),
+ array(
+ '/* bitwiseAndE */',
+ false,
+ ),
+ array(
+ '/* bitwiseAndF */',
+ false,
+ ),
+ array(
+ '/* bitwiseAndG */',
+ false,
+ ),
+ array(
+ '/* bitwiseAndH */',
+ false,
+ ),
+ array(
+ '/* bitwiseAndI */',
+ false,
+ ),
+ array(
+ '/* functionReturnByReference */',
+ true,
+ ),
+ array(
+ '/* functionPassByReferenceA */',
+ true,
+ ),
+ array(
+ '/* functionPassByReferenceB */',
+ true,
+ ),
+ array(
+ '/* functionPassByReferenceC */',
+ true,
+ ),
+ array(
+ '/* functionPassByReferenceD */',
+ true,
+ ),
+ array(
+ '/* functionPassByReferenceE */',
+ true,
+ ),
+ array(
+ '/* functionPassByReferenceF */',
+ true,
+ ),
+ array(
+ '/* functionPassByReferenceG */',
+ true,
+ ),
+ array(
+ '/* foreachValueByReference */',
+ true,
+ ),
+ array(
+ '/* foreachKeyByReference */',
+ true,
+ ),
+ array(
+ '/* arrayValueByReferenceA */',
+ true,
+ ),
+ array(
+ '/* arrayValueByReferenceB */',
+ true,
+ ),
+ array(
+ '/* arrayValueByReferenceC */',
+ true,
+ ),
+ array(
+ '/* arrayValueByReferenceD */',
+ true,
+ ),
+ array(
+ '/* arrayValueByReferenceE */',
+ true,
+ ),
+ array(
+ '/* arrayValueByReferenceF */',
+ true,
+ ),
+ array(
+ '/* arrayValueByReferenceG */',
+ true,
+ ),
+ array(
+ '/* arrayValueByReferenceH */',
+ true,
+ ),
+ array(
+ '/* assignByReferenceA */',
+ true,
+ ),
+ array(
+ '/* assignByReferenceB */',
+ true,
+ ),
+ array(
+ '/* assignByReferenceC */',
+ true,
+ ),
+ array(
+ '/* assignByReferenceD */',
+ true,
+ ),
+ array(
+ '/* assignByReferenceE */',
+ true,
+ ),
+ array(
+ '/* passByReferenceA */',
+ true,
+ ),
+ array(
+ '/* passByReferenceB */',
+ true,
+ ),
+ array(
+ '/* passByReferenceC */',
+ true,
+ ),
+ array(
+ '/* passByReferenceD */',
+ true,
+ ),
+ array(
+ '/* passByReferenceE */',
+ true,
+ ),
+ array(
+ '/* passByReferenceF */',
+ true,
+ ),
+ array(
+ '/* passByReferenceG */',
+ true,
+ ),
+ array(
+ '/* passByReferenceH */',
+ true,
+ ),
+ array(
+ '/* passByReferenceI */',
+ true,
+ ),
+ array(
+ '/* passByReferenceJ */',
+ true,
+ ),
+ array(
+ '/* newByReferenceA */',
+ true,
+ ),
+ array(
+ '/* newByReferenceB */',
+ true,
+ ),
+ array(
+ '/* useByReference */',
+ true,
+ ),
+ );
+
+ }//end dataIsReference()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Tests for the \PHP_CodeSniffer\Util\Common::isCamelCaps method.
+ *
+ * @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\Tests\Core;
+
+use PHP_CodeSniffer\Util\Common;
+use PHPUnit\Framework\TestCase;
+
+class IsCamelCapsTest extends TestCase
+{
+
+
+ /**
+ * Test valid public function/method names.
+ *
+ * @return void
+ */
+ public function testValidNotClassFormatPublic()
+ {
+ $this->assertTrue(Common::isCamelCaps('thisIsCamelCaps', false, true, true));
+ $this->assertTrue(Common::isCamelCaps('thisISCamelCaps', false, true, false));
+
+ }//end testValidNotClassFormatPublic()
+
+
+ /**
+ * Test invalid public function/method names.
+ *
+ * @return void
+ */
+ public function testInvalidNotClassFormatPublic()
+ {
+ $this->assertFalse(Common::isCamelCaps('_thisIsCamelCaps', false, true, true));
+ $this->assertFalse(Common::isCamelCaps('thisISCamelCaps', false, true, true));
+ $this->assertFalse(Common::isCamelCaps('ThisIsCamelCaps', false, true, true));
+
+ $this->assertFalse(Common::isCamelCaps('3thisIsCamelCaps', false, true, true));
+ $this->assertFalse(Common::isCamelCaps('*thisIsCamelCaps', false, true, true));
+ $this->assertFalse(Common::isCamelCaps('-thisIsCamelCaps', false, true, true));
+
+ $this->assertFalse(Common::isCamelCaps('this*IsCamelCaps', false, true, true));
+ $this->assertFalse(Common::isCamelCaps('this-IsCamelCaps', false, true, true));
+ $this->assertFalse(Common::isCamelCaps('this_IsCamelCaps', false, true, true));
+ $this->assertFalse(Common::isCamelCaps('this_is_camel_caps', false, true, true));
+
+ }//end testInvalidNotClassFormatPublic()
+
+
+ /**
+ * Test valid private method names.
+ *
+ * @return void
+ */
+ public function testValidNotClassFormatPrivate()
+ {
+ $this->assertTrue(Common::isCamelCaps('_thisIsCamelCaps', false, false, true));
+ $this->assertTrue(Common::isCamelCaps('_thisISCamelCaps', false, false, false));
+ $this->assertTrue(Common::isCamelCaps('_i18N', false, false, true));
+ $this->assertTrue(Common::isCamelCaps('_i18n', false, false, true));
+
+ }//end testValidNotClassFormatPrivate()
+
+
+ /**
+ * Test invalid private method names.
+ *
+ * @return void
+ */
+ public function testInvalidNotClassFormatPrivate()
+ {
+ $this->assertFalse(Common::isCamelCaps('thisIsCamelCaps', false, false, true));
+ $this->assertFalse(Common::isCamelCaps('_thisISCamelCaps', false, false, true));
+ $this->assertFalse(Common::isCamelCaps('_ThisIsCamelCaps', false, false, true));
+ $this->assertFalse(Common::isCamelCaps('__thisIsCamelCaps', false, false, true));
+ $this->assertFalse(Common::isCamelCaps('__thisISCamelCaps', false, false, false));
+
+ $this->assertFalse(Common::isCamelCaps('3thisIsCamelCaps', false, false, true));
+ $this->assertFalse(Common::isCamelCaps('*thisIsCamelCaps', false, false, true));
+ $this->assertFalse(Common::isCamelCaps('-thisIsCamelCaps', false, false, true));
+ $this->assertFalse(Common::isCamelCaps('_this_is_camel_caps', false, false, true));
+
+ }//end testInvalidNotClassFormatPrivate()
+
+
+ /**
+ * Test valid class names.
+ *
+ * @return void
+ */
+ public function testValidClassFormatPublic()
+ {
+ $this->assertTrue(Common::isCamelCaps('ThisIsCamelCaps', true, true, true));
+ $this->assertTrue(Common::isCamelCaps('ThisISCamelCaps', true, true, false));
+ $this->assertTrue(Common::isCamelCaps('This3IsCamelCaps', true, true, false));
+
+ }//end testValidClassFormatPublic()
+
+
+ /**
+ * Test invalid class names.
+ *
+ * @return void
+ */
+ public function testInvalidClassFormat()
+ {
+ $this->assertFalse(Common::isCamelCaps('thisIsCamelCaps', true));
+ $this->assertFalse(Common::isCamelCaps('This-IsCamelCaps', true));
+ $this->assertFalse(Common::isCamelCaps('This_Is_Camel_Caps', true));
+
+ }//end testInvalidClassFormat()
+
+
+ /**
+ * Test invalid class names with the private flag set.
+ *
+ * Note that the private flag is ignored if the class format
+ * flag is set, so these names are all invalid.
+ *
+ * @return void
+ */
+ public function testInvalidClassFormatPrivate()
+ {
+ $this->assertFalse(Common::isCamelCaps('_ThisIsCamelCaps', true, true));
+ $this->assertFalse(Common::isCamelCaps('_ThisIsCamelCaps', true, false));
+
+ }//end testInvalidClassFormatPrivate()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * An abstract class that all sniff unit tests must extend.
+ *
+ * A sniff unit test checks a .inc file for expected violations of a single
+ * coding standard. Expected errors and warnings that are not found, or
+ * warnings and errors that are not expected, are considered test failures.
+ *
+ * @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\Tests\Standards;
+
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Ruleset;
+use PHP_CodeSniffer\Files\LocalFile;
+use PHP_CodeSniffer\Util\Common;
+use PHPUnit\Framework\TestCase;
+
+abstract class AbstractSniffUnitTest extends TestCase
+{
+
+ /**
+ * Enable or disable the backup and restoration of the $GLOBALS array.
+ * Overwrite this attribute in a child class of TestCase.
+ * Setting this attribute in setUp() has no effect!
+ *
+ * @var boolean
+ */
+ protected $backupGlobals = false;
+
+ /**
+ * The path to the standard's main directory.
+ *
+ * @var string
+ */
+ public $standardsDir = null;
+
+ /**
+ * The path to the standard's test directory.
+ *
+ * @var string
+ */
+ public $testsDir = null;
+
+
+ /**
+ * Sets up this unit test.
+ *
+ * @return void
+ */
+ protected function setUp()
+ {
+ $class = get_class($this);
+ $this->standardsDir = $GLOBALS['PHP_CODESNIFFER_STANDARD_DIRS'][$class];
+ $this->testsDir = $GLOBALS['PHP_CODESNIFFER_TEST_DIRS'][$class];
+
+ }//end setUp()
+
+
+ /**
+ * Get a list of all test files to check.
+ *
+ * These will have the same base as the sniff name but different extensions.
+ * We ignore the .php file as it is the class.
+ *
+ * @param string $testFileBase The base path that the unit tests files will have.
+ *
+ * @return string[]
+ */
+ protected function getTestFiles($testFileBase)
+ {
+ $testFiles = array();
+
+ $dir = substr($testFileBase, 0, strrpos($testFileBase, DIRECTORY_SEPARATOR));
+ $di = new \DirectoryIterator($dir);
+
+ foreach ($di as $file) {
+ $path = $file->getPathname();
+ if (substr($path, 0, strlen($testFileBase)) === $testFileBase) {
+ if ($path !== $testFileBase.'php' && substr($path, -5) !== 'fixed') {
+ $testFiles[] = $path;
+ }
+ }
+ }
+
+ // Put them in order.
+ sort($testFiles);
+
+ return $testFiles;
+
+ }//end getTestFiles()
+
+
+ /**
+ * Should this test be skipped for some reason.
+ *
+ * @return boolean
+ */
+ protected function shouldSkipTest()
+ {
+ return false;
+
+ }//end shouldSkipTest()
+
+
+ /**
+ * Tests the extending classes Sniff class.
+ *
+ * @return void
+ * @throws \PHPUnit\Framework\Exception
+ */
+ final public function testSniff()
+ {
+ // Skip this test if we can't run in this environment.
+ if ($this->shouldSkipTest() === true) {
+ $this->markTestSkipped();
+ }
+
+ $sniffCode = Common::getSniffCode(get_class($this));
+ list($standardName, $categoryName, $sniffName) = explode('.', $sniffCode);
+
+ $testFileBase = $this->testsDir.$categoryName.DIRECTORY_SEPARATOR.$sniffName.'UnitTest.';
+
+ // Get a list of all test files to check.
+ $testFiles = $this->getTestFiles($testFileBase);
+
+ if (isset($GLOBALS['PHP_CODESNIFFER_CONFIG']) === true) {
+ $config = $GLOBALS['PHP_CODESNIFFER_CONFIG'];
+ } else {
+ $config = new Config();
+ $config->cache = false;
+ $GLOBALS['PHP_CODESNIFFER_CONFIG'] = $config;
+ }
+
+ $config->standards = array($standardName);
+ $config->sniffs = array($sniffCode);
+ $config->ignored = array();
+
+ if (isset($GLOBALS['PHP_CODESNIFFER_RULESETS']) === false) {
+ $GLOBALS['PHP_CODESNIFFER_RULESETS'] = array();
+ }
+
+ if (isset($GLOBALS['PHP_CODESNIFFER_RULESETS'][$standardName]) === false) {
+ $ruleset = new Ruleset($config);
+ $GLOBALS['PHP_CODESNIFFER_RULESETS'][$standardName] = $ruleset;
+ }
+
+ $ruleset = $GLOBALS['PHP_CODESNIFFER_RULESETS'][$standardName];
+
+ $sniffFile = $this->standardsDir.DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$categoryName.DIRECTORY_SEPARATOR.$sniffName.'Sniff.php';
+
+ $sniffClassName = substr(get_class($this), 0, -8).'Sniff';
+ $sniffClassName = str_replace('\Tests\\', '\Sniffs\\', $sniffClassName);
+ $sniffClassName = Common::cleanSniffClass($sniffClassName);
+
+ $restrictions = array(strtolower($sniffClassName) => true);
+ $ruleset->registerSniffs(array($sniffFile), $restrictions, array());
+ $ruleset->populateTokenListeners();
+
+ $failureMessages = array();
+ foreach ($testFiles as $testFile) {
+ $filename = basename($testFile);
+ $oldConfig = $config->getSettings();
+
+ try {
+ $this->setCliValues($filename, $config);
+ $phpcsFile = new LocalFile($testFile, $ruleset, $config);
+ $phpcsFile->process();
+ } catch (RuntimeException $e) {
+ $this->fail('An unexpected exception has been caught: '.$e->getMessage());
+ }
+
+ $failures = $this->generateFailureMessages($phpcsFile);
+ $failureMessages = array_merge($failureMessages, $failures);
+
+ if ($phpcsFile->getFixableCount() > 0) {
+ // Attempt to fix the errors.
+ $phpcsFile->fixer->fixFile();
+ $fixable = $phpcsFile->getFixableCount();
+ if ($fixable > 0) {
+ $failureMessages[] = "Failed to fix $fixable fixable violations in $filename";
+ }
+
+ // Check for a .fixed file to check for accuracy of fixes.
+ $fixedFile = $testFile.'.fixed';
+ if (file_exists($fixedFile) === true) {
+ $diff = $phpcsFile->fixer->generateDiff($fixedFile);
+ if (trim($diff) !== '') {
+ $filename = basename($testFile);
+ $fixedFilename = basename($fixedFile);
+ $failureMessages[] = "Fixed version of $filename does not match expected version in $fixedFilename; the diff is\n$diff";
+ }
+ }
+ }
+
+ // Restore the config.
+ $config->setSettings($oldConfig);
+ }//end foreach
+
+ if (empty($failureMessages) === false) {
+ $this->fail(implode(PHP_EOL, $failureMessages));
+ }
+
+ }//end testSniff()
+
+
+ /**
+ * Generate a list of test failures for a given sniffed file.
+ *
+ * @param \PHP_CodeSniffer\Files\LocalFile $file The file being tested.
+ *
+ * @return array
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException
+ */
+ public function generateFailureMessages(LocalFile $file)
+ {
+ $testFile = $file->getFilename();
+
+ $foundErrors = $file->getErrors();
+ $foundWarnings = $file->getWarnings();
+ $expectedErrors = $this->getErrorList(basename($testFile));
+ $expectedWarnings = $this->getWarningList(basename($testFile));
+
+ if (is_array($expectedErrors) === false) {
+ throw new RuntimeException('getErrorList() must return an array');
+ }
+
+ if (is_array($expectedWarnings) === false) {
+ throw new RuntimeException('getWarningList() must return an array');
+ }
+
+ /*
+ We merge errors and warnings together to make it easier
+ to iterate over them and produce the errors string. In this way,
+ we can report on errors and warnings in the same line even though
+ it's not really structured to allow that.
+ */
+
+ $allProblems = array();
+ $failureMessages = array();
+
+ foreach ($foundErrors as $line => $lineErrors) {
+ foreach ($lineErrors as $column => $errors) {
+ if (isset($allProblems[$line]) === false) {
+ $allProblems[$line] = array(
+ 'expected_errors' => 0,
+ 'expected_warnings' => 0,
+ 'found_errors' => array(),
+ 'found_warnings' => array(),
+ );
+ }
+
+ $foundErrorsTemp = array();
+ foreach ($allProblems[$line]['found_errors'] as $foundError) {
+ $foundErrorsTemp[] = $foundError;
+ }
+
+ $errorsTemp = array();
+ foreach ($errors as $foundError) {
+ $errorsTemp[] = $foundError['message'].' ('.$foundError['source'].')';
+
+ $source = $foundError['source'];
+ if (in_array($source, $GLOBALS['PHP_CODESNIFFER_SNIFF_CODES']) === false) {
+ $GLOBALS['PHP_CODESNIFFER_SNIFF_CODES'][] = $source;
+ }
+
+ if ($foundError['fixable'] === true
+ && in_array($source, $GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES']) === false
+ ) {
+ $GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES'][] = $source;
+ }
+ }
+
+ $allProblems[$line]['found_errors'] = array_merge($foundErrorsTemp, $errorsTemp);
+ }//end foreach
+
+ if (isset($expectedErrors[$line]) === true) {
+ $allProblems[$line]['expected_errors'] = $expectedErrors[$line];
+ } else {
+ $allProblems[$line]['expected_errors'] = 0;
+ }
+
+ unset($expectedErrors[$line]);
+ }//end foreach
+
+ foreach ($expectedErrors as $line => $numErrors) {
+ if (isset($allProblems[$line]) === false) {
+ $allProblems[$line] = array(
+ 'expected_errors' => 0,
+ 'expected_warnings' => 0,
+ 'found_errors' => array(),
+ 'found_warnings' => array(),
+ );
+ }
+
+ $allProblems[$line]['expected_errors'] = $numErrors;
+ }
+
+ foreach ($foundWarnings as $line => $lineWarnings) {
+ foreach ($lineWarnings as $column => $warnings) {
+ if (isset($allProblems[$line]) === false) {
+ $allProblems[$line] = array(
+ 'expected_errors' => 0,
+ 'expected_warnings' => 0,
+ 'found_errors' => array(),
+ 'found_warnings' => array(),
+ );
+ }
+
+ $foundWarningsTemp = array();
+ foreach ($allProblems[$line]['found_warnings'] as $foundWarning) {
+ $foundWarningsTemp[] = $foundWarning;
+ }
+
+ $warningsTemp = array();
+ foreach ($warnings as $warning) {
+ $warningsTemp[] = $warning['message'].' ('.$warning['source'].')';
+ }
+
+ $allProblems[$line]['found_warnings'] = array_merge($foundWarningsTemp, $warningsTemp);
+ }//end foreach
+
+ if (isset($expectedWarnings[$line]) === true) {
+ $allProblems[$line]['expected_warnings'] = $expectedWarnings[$line];
+ } else {
+ $allProblems[$line]['expected_warnings'] = 0;
+ }
+
+ unset($expectedWarnings[$line]);
+ }//end foreach
+
+ foreach ($expectedWarnings as $line => $numWarnings) {
+ if (isset($allProblems[$line]) === false) {
+ $allProblems[$line] = array(
+ 'expected_errors' => 0,
+ 'expected_warnings' => 0,
+ 'found_errors' => array(),
+ 'found_warnings' => array(),
+ );
+ }
+
+ $allProblems[$line]['expected_warnings'] = $numWarnings;
+ }
+
+ // Order the messages by line number.
+ ksort($allProblems);
+
+ foreach ($allProblems as $line => $problems) {
+ $numErrors = count($problems['found_errors']);
+ $numWarnings = count($problems['found_warnings']);
+ $expectedErrors = $problems['expected_errors'];
+ $expectedWarnings = $problems['expected_warnings'];
+
+ $errors = '';
+ $foundString = '';
+
+ if ($expectedErrors !== $numErrors || $expectedWarnings !== $numWarnings) {
+ $lineMessage = "[LINE $line]";
+ $expectedMessage = 'Expected ';
+ $foundMessage = 'in '.basename($testFile).' but found ';
+
+ if ($expectedErrors !== $numErrors) {
+ $expectedMessage .= "$expectedErrors error(s)";
+ $foundMessage .= "$numErrors error(s)";
+ if ($numErrors !== 0) {
+ $foundString .= 'error(s)';
+ $errors .= implode(PHP_EOL.' -> ', $problems['found_errors']);
+ }
+
+ if ($expectedWarnings !== $numWarnings) {
+ $expectedMessage .= ' and ';
+ $foundMessage .= ' and ';
+ if ($numWarnings !== 0) {
+ if ($foundString !== '') {
+ $foundString .= ' and ';
+ }
+ }
+ }
+ }
+
+ if ($expectedWarnings !== $numWarnings) {
+ $expectedMessage .= "$expectedWarnings warning(s)";
+ $foundMessage .= "$numWarnings warning(s)";
+ if ($numWarnings !== 0) {
+ $foundString .= 'warning(s)';
+ if (empty($errors) === false) {
+ $errors .= PHP_EOL.' -> ';
+ }
+
+ $errors .= implode(PHP_EOL.' -> ', $problems['found_warnings']);
+ }
+ }
+
+ $fullMessage = "$lineMessage $expectedMessage $foundMessage.";
+ if ($errors !== '') {
+ $fullMessage .= " The $foundString found were:".PHP_EOL." -> $errors";
+ }
+
+ $failureMessages[] = $fullMessage;
+ }//end if
+ }//end foreach
+
+ return $failureMessages;
+
+ }//end generateFailureMessages()
+
+
+ /**
+ * Get a list of CLI values to set before the file is tested.
+ *
+ * @param string $filename The name of the file being tested.
+ * @param \PHP_CodeSniffer\Config $config The config data for the run.
+ *
+ * @return void
+ */
+ public function setCliValues($filename, $config)
+ {
+ return;
+
+ }//end setCliValues()
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ abstract protected function getErrorList();
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array<int, int>
+ */
+ abstract protected function getWarningList();
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * A test class for testing all sniffs for installed standards.
+ *
+ * @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\Tests\Standards;
+
+use PHP_CodeSniffer\Util\Standards;
+use PHP_CodeSniffer\Autoload;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHPUnit\TextUI\TestRunner;
+use PHPUnit\Framework\TestSuite;
+
+class AllSniffs
+{
+
+
+ /**
+ * Prepare the test runner.
+ *
+ * @return void
+ */
+ public static function main()
+ {
+ TestRunner::run(self::suite());
+
+ }//end main()
+
+
+ /**
+ * Add all sniff unit tests into a test suite.
+ *
+ * Sniff unit tests are found by recursing through the 'Tests' directory
+ * of each installed coding standard.
+ *
+ * @return \PHPUnit\Framework\TestSuite
+ */
+ public static function suite()
+ {
+ $GLOBALS['PHP_CODESNIFFER_SNIFF_CODES'] = array();
+ $GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES'] = array();
+
+ $suite = new TestSuite('PHP CodeSniffer Standards');
+
+ $isInstalled = !is_file(__DIR__.'/../../autoload.php');
+
+ // Optionally allow for ignoring the tests for one or more standards.
+ $ignoreTestsForStandards = getenv('PHPCS_IGNORE_TESTS');
+ if ($ignoreTestsForStandards === false) {
+ $ignoreTestsForStandards = array();
+ } else {
+ $ignoreTestsForStandards = explode(',', $ignoreTestsForStandards);
+ }
+
+ $installedStandards = Standards::getInstalledStandardDetails(true);
+
+ foreach ($installedStandards as $standard => $details) {
+ Autoload::addSearchPath($details['path'], $details['namespace']);
+
+ // If the test is running PEAR installed, the built-in standards
+ // are split into different directories; one for the sniffs and
+ // a different file system location for tests.
+ if ($isInstalled === true && is_dir(dirname($details['path']).DIRECTORY_SEPARATOR.'Generic') === true) {
+ $testPath = realpath(__DIR__.'/../../src/Standards/'.$standard);
+ } else {
+ $testPath = $details['path'];
+ }
+
+ if (in_array($standard, $ignoreTestsForStandards) === true) {
+ continue;
+ }
+
+ $testsDir = $testPath.DIRECTORY_SEPARATOR.'Tests'.DIRECTORY_SEPARATOR;
+ if (is_dir($testsDir) === false) {
+ // No tests for this standard.
+ continue;
+ }
+
+ $di = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($testsDir));
+
+ foreach ($di as $file) {
+ // Skip hidden files.
+ if (substr($file->getFilename(), 0, 1) === '.') {
+ continue;
+ }
+
+ // Tests must have the extension 'php'.
+ $parts = explode('.', $file);
+ $ext = array_pop($parts);
+ if ($ext !== 'php') {
+ continue;
+ }
+
+ $className = Autoload::loadFile($file->getPathname());
+ $GLOBALS['PHP_CODESNIFFER_STANDARD_DIRS'][$className] = $details['path'];
+ $GLOBALS['PHP_CODESNIFFER_TEST_DIRS'][$className] = $testsDir;
+ $suite->addTestSuite($className);
+ }
+ }//end foreach
+
+ return $suite;
+
+ }//end suite()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * A PHP_CodeSniffer specific test suite for PHPUnit.
+ *
+ * @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\Tests;
+
+use PHPUnit\Framework\TestSuite as PHPUnit_TestSuite;
+use PHPUnit\Framework\TestResult;
+
+class TestSuite extends PHPUnit_TestSuite
+{
+
+
+ /**
+ * Runs the tests and collects their result in a TestResult.
+ *
+ * @param \PHPUnit\Framework\TestResult $result A test result.
+ * @param mixed $filter The filter passed to each test.
+ *
+ * @return \PHPUnit\Framework\TestResult
+ */
+ public function run(TestResult $result=null, $filter=false)
+ {
+ $result = parent::run($result, $filter);
+
+ $codes = count($GLOBALS['PHP_CODESNIFFER_SNIFF_CODES']);
+
+ echo PHP_EOL.PHP_EOL;
+ echo "Tests generated $codes unique error codes";
+ if ($codes > 0) {
+ $fixes = count($GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES']);
+ $percent = round(($fixes / $codes * 100), 2);
+ echo "; $fixes were fixable ($percent%)";
+ }
+
+ return $result;
+
+ }//end run()
+
+
+}//end class
--- /dev/null
+<?php
+/**
+ * Bootstrap file for PHP_CodeSniffer unit tests.
+ *
+ * @author Greg Sherwood <gsherwood@squiz.net>
+ * @copyright 2006-2017 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+if (defined('PHP_CODESNIFFER_IN_TESTS') === false) {
+ define('PHP_CODESNIFFER_IN_TESTS', true);
+}
+
+if (defined('PHP_CODESNIFFER_CBF') === false) {
+ define('PHP_CODESNIFFER_CBF', false);
+}
+
+if (defined('PHP_CODESNIFFER_VERBOSITY') === false) {
+ define('PHP_CODESNIFFER_VERBOSITY', 0);
+}
+
+if (is_file(__DIR__.'/../autoload.php') === true) {
+ include_once __DIR__.'/../autoload.php';
+} else {
+ include_once 'PHP/CodeSniffer/autoload.php';
+}
+
+$tokens = new \PHP_CodeSniffer\Util\Tokens();
+
+
+// Compatibility for PHPUnit < 6 and PHPUnit 6+.
+if (class_exists('PHPUnit_Framework_TestSuite') === true && class_exists('PHPUnit\Framework\TestSuite') === false) {
+ class_alias('PHPUnit_Framework_TestSuite', 'PHPUnit'.'\Framework\TestSuite');
+}
+
+if (class_exists('PHPUnit_Framework_TestCase') === true && class_exists('PHPUnit\Framework\TestCase') === false) {
+ class_alias('PHPUnit_Framework_TestCase', 'PHPUnit'.'\Framework\TestCase');
+}
+
+if (class_exists('PHPUnit_TextUI_TestRunner') === true && class_exists('PHPUnit\TextUI\TestRunner') === false) {
+ class_alias('PHPUnit_TextUI_TestRunner', 'PHPUnit'.'\TextUI\TestRunner');
+}
+
+if (class_exists('PHPUnit_Framework_TestResult') === true && class_exists('PHPUnit\Framework\TestResult') === false) {
+ class_alias('PHPUnit_Framework_TestResult', 'PHPUnit'.'\Framework\TestResult');
+}
--- /dev/null
+Copyright (c) 2014-2016 Fabien Potencier
+
+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.
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Polyfill\Mbstring;
+
+/**
+ * Partial mbstring implementation in PHP, iconv based, UTF-8 centric.
+ *
+ * Implemented:
+ * - mb_chr - Returns a specific character from its Unicode code point
+ * - mb_convert_encoding - Convert character encoding
+ * - mb_convert_variables - Convert character code in variable(s)
+ * - mb_decode_mimeheader - Decode string in MIME header field
+ * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED
+ * - mb_convert_case - Perform case folding on a string
+ * - mb_get_info - Get internal settings of mbstring
+ * - mb_http_input - Detect HTTP input character encoding
+ * - mb_http_output - Set/Get HTTP output character encoding
+ * - mb_internal_encoding - Set/Get internal character encoding
+ * - mb_list_encodings - Returns an array of all supported encodings
+ * - mb_ord - Returns the Unicode code point of a character
+ * - mb_output_handler - Callback function converts character encoding in output buffer
+ * - mb_scrub - Replaces ill-formed byte sequences with substitute characters
+ * - mb_strlen - Get string length
+ * - mb_strpos - Find position of first occurrence of string in a string
+ * - mb_strrpos - Find position of last occurrence of a string in a string
+ * - mb_strtolower - Make a string lowercase
+ * - mb_strtoupper - Make a string uppercase
+ * - mb_substitute_character - Set/Get substitution character
+ * - mb_substr - Get part of string
+ * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive
+ * - mb_stristr - Finds first occurrence of a string within another, case insensitive
+ * - mb_strrchr - Finds the last occurrence of a character in a string within another
+ * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive
+ * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive
+ * - mb_strstr - Finds first occurrence of a string within anothers
+ * - mb_strwidth - Return width of string
+ * - mb_substr_count - Count the number of substring occurrences
+ *
+ * Not implemented:
+ * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more)
+ * - mb_decode_numericentity - Decode HTML numeric string reference to character
+ * - mb_encode_numericentity - Encode character to HTML numeric string reference
+ * - mb_ereg_* - Regular expression with multibyte support
+ * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable
+ * - mb_preferred_mime_name - Get MIME charset string
+ * - mb_regex_encoding - Returns current encoding for multibyte regex as string
+ * - mb_regex_set_options - Set/Get the default options for mbregex functions
+ * - mb_send_mail - Send encoded mail
+ * - mb_split - Split multibyte string using regular expression
+ * - mb_strcut - Get part of string
+ * - mb_strimwidth - Get truncated string with specified width
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @internal
+ */
+final class Mbstring
+{
+ const MB_CASE_FOLD = PHP_INT_MAX;
+
+ private static $encodingList = array('ASCII', 'UTF-8');
+ private static $language = 'neutral';
+ private static $internalEncoding = 'UTF-8';
+ private static $caseFold = array(
+ array('µ','ſ',"\xCD\x85",'ς',"\xCF\x90","\xCF\x91","\xCF\x95","\xCF\x96","\xCF\xB0","\xCF\xB1","\xCF\xB5","\xE1\xBA\x9B","\xE1\xBE\xBE"),
+ array('μ','s','ι', 'σ','β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1",'ι'),
+ );
+
+ public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
+ {
+ if (is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) {
+ $fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
+ } else {
+ $fromEncoding = self::getEncoding($fromEncoding);
+ }
+
+ $toEncoding = self::getEncoding($toEncoding);
+
+ if ('BASE64' === $fromEncoding) {
+ $s = base64_decode($s);
+ $fromEncoding = $toEncoding;
+ }
+
+ if ('BASE64' === $toEncoding) {
+ return base64_encode($s);
+ }
+
+ if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) {
+ if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) {
+ $fromEncoding = 'Windows-1252';
+ }
+ if ('UTF-8' !== $fromEncoding) {
+ $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s);
+ }
+
+ return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s);
+ }
+
+ if ('HTML-ENTITIES' === $fromEncoding) {
+ $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8');
+ $fromEncoding = 'UTF-8';
+ }
+
+ return iconv($fromEncoding, $toEncoding.'//IGNORE', $s);
+ }
+
+ public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null)
+ {
+ $vars = array(&$a, &$b, &$c, &$d, &$e, &$f);
+
+ $ok = true;
+ array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
+ if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
+ $ok = false;
+ }
+ });
+
+ return $ok ? $fromEncoding : false;
+ }
+
+ public static function mb_decode_mimeheader($s)
+ {
+ return iconv_mime_decode($s, 2, self::$internalEncoding);
+ }
+
+ public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)
+ {
+ trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING);
+ }
+
+ public static function mb_convert_case($s, $mode, $encoding = null)
+ {
+ if ('' === $s .= '') {
+ return '';
+ }
+
+ $encoding = self::getEncoding($encoding);
+
+ if ('UTF-8' === $encoding) {
+ $encoding = null;
+ if (!preg_match('//u', $s)) {
+ $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
+ }
+ } else {
+ $s = iconv($encoding, 'UTF-8//IGNORE', $s);
+ }
+
+ if (MB_CASE_TITLE == $mode) {
+ $s = preg_replace_callback('/\b\p{Ll}/u', array(__CLASS__, 'title_case_upper'), $s);
+ $s = preg_replace_callback('/\B[\p{Lu}\p{Lt}]+/u', array(__CLASS__, 'title_case_lower'), $s);
+ } else {
+ if (MB_CASE_UPPER == $mode) {
+ static $upper = null;
+ if (null === $upper) {
+ $upper = self::getData('upperCase');
+ }
+ $map = $upper;
+ } else {
+ if (self::MB_CASE_FOLD === $mode) {
+ $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s);
+ }
+
+ static $lower = null;
+ if (null === $lower) {
+ $lower = self::getData('lowerCase');
+ }
+ $map = $lower;
+ }
+
+ static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
+
+ $i = 0;
+ $len = strlen($s);
+
+ while ($i < $len) {
+ $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
+ $uchr = substr($s, $i, $ulen);
+ $i += $ulen;
+
+ if (isset($map[$uchr])) {
+ $uchr = $map[$uchr];
+ $nlen = strlen($uchr);
+
+ if ($nlen == $ulen) {
+ $nlen = $i;
+ do {
+ $s[--$nlen] = $uchr[--$ulen];
+ } while ($ulen);
+ } else {
+ $s = substr_replace($s, $uchr, $i - $ulen, $ulen);
+ $len += $nlen - $ulen;
+ $i += $nlen - $ulen;
+ }
+ }
+ }
+ }
+
+ if (null === $encoding) {
+ return $s;
+ }
+
+ return iconv('UTF-8', $encoding.'//IGNORE', $s);
+ }
+
+ public static function mb_internal_encoding($encoding = null)
+ {
+ if (null === $encoding) {
+ return self::$internalEncoding;
+ }
+
+ $encoding = self::getEncoding($encoding);
+
+ if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) {
+ self::$internalEncoding = $encoding;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function mb_language($lang = null)
+ {
+ if (null === $lang) {
+ return self::$language;
+ }
+
+ switch ($lang = strtolower($lang)) {
+ case 'uni':
+ case 'neutral':
+ self::$language = $lang;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function mb_list_encodings()
+ {
+ return array('UTF-8');
+ }
+
+ public static function mb_encoding_aliases($encoding)
+ {
+ switch (strtoupper($encoding)) {
+ case 'UTF8':
+ case 'UTF-8':
+ return array('utf8');
+ }
+
+ return false;
+ }
+
+ public static function mb_check_encoding($var = null, $encoding = null)
+ {
+ if (null === $encoding) {
+ if (null === $var) {
+ return false;
+ }
+ $encoding = self::$internalEncoding;
+ }
+
+ return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var);
+ }
+
+ public static function mb_detect_encoding($str, $encodingList = null, $strict = false)
+ {
+ if (null === $encodingList) {
+ $encodingList = self::$encodingList;
+ } else {
+ if (!is_array($encodingList)) {
+ $encodingList = array_map('trim', explode(',', $encodingList));
+ }
+ $encodingList = array_map('strtoupper', $encodingList);
+ }
+
+ foreach ($encodingList as $enc) {
+ switch ($enc) {
+ case 'ASCII':
+ if (!preg_match('/[\x80-\xFF]/', $str)) {
+ return $enc;
+ }
+ break;
+
+ case 'UTF8':
+ case 'UTF-8':
+ if (preg_match('//u', $str)) {
+ return 'UTF-8';
+ }
+ break;
+
+ default:
+ if (0 === strncmp($enc, 'ISO-8859-', 9)) {
+ return $enc;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public static function mb_detect_order($encodingList = null)
+ {
+ if (null === $encodingList) {
+ return self::$encodingList;
+ }
+
+ if (!is_array($encodingList)) {
+ $encodingList = array_map('trim', explode(',', $encodingList));
+ }
+ $encodingList = array_map('strtoupper', $encodingList);
+
+ foreach ($encodingList as $enc) {
+ switch ($enc) {
+ default:
+ if (strncmp($enc, 'ISO-8859-', 9)) {
+ return false;
+ }
+ case 'ASCII':
+ case 'UTF8':
+ case 'UTF-8':
+ }
+ }
+
+ self::$encodingList = $encodingList;
+
+ return true;
+ }
+
+ public static function mb_strlen($s, $encoding = null)
+ {
+ $encoding = self::getEncoding($encoding);
+ if ('CP850' === $encoding || 'ASCII' === $encoding) {
+ return strlen($s);
+ }
+
+ return @iconv_strlen($s, $encoding);
+ }
+
+ public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
+ {
+ $encoding = self::getEncoding($encoding);
+ if ('CP850' === $encoding || 'ASCII' === $encoding) {
+ return strpos($haystack, $needle, $offset);
+ }
+
+ if ('' === $needle .= '') {
+ trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING);
+
+ return false;
+ }
+
+ return iconv_strpos($haystack, $needle, $offset, $encoding);
+ }
+
+ public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
+ {
+ $encoding = self::getEncoding($encoding);
+ if ('CP850' === $encoding || 'ASCII' === $encoding) {
+ return strrpos($haystack, $needle, $offset);
+ }
+
+ if ($offset != (int) $offset) {
+ $offset = 0;
+ } elseif ($offset = (int) $offset) {
+ if ($offset < 0) {
+ $haystack = self::mb_substr($haystack, 0, $offset, $encoding);
+ $offset = 0;
+ } else {
+ $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
+ }
+ }
+
+ $pos = iconv_strrpos($haystack, $needle, $encoding);
+
+ return false !== $pos ? $offset + $pos : false;
+ }
+
+ public static function mb_strtolower($s, $encoding = null)
+ {
+ return self::mb_convert_case($s, MB_CASE_LOWER, $encoding);
+ }
+
+ public static function mb_strtoupper($s, $encoding = null)
+ {
+ return self::mb_convert_case($s, MB_CASE_UPPER, $encoding);
+ }
+
+ public static function mb_substitute_character($c = null)
+ {
+ if (0 === strcasecmp($c, 'none')) {
+ return true;
+ }
+
+ return null !== $c ? false : 'none';
+ }
+
+ public static function mb_substr($s, $start, $length = null, $encoding = null)
+ {
+ $encoding = self::getEncoding($encoding);
+ if ('CP850' === $encoding || 'ASCII' === $encoding) {
+ return substr($s, $start, null === $length ? 2147483647 : $length);
+ }
+
+ if ($start < 0) {
+ $start = iconv_strlen($s, $encoding) + $start;
+ if ($start < 0) {
+ $start = 0;
+ }
+ }
+
+ if (null === $length) {
+ $length = 2147483647;
+ } elseif ($length < 0) {
+ $length = iconv_strlen($s, $encoding) + $length - $start;
+ if ($length < 0) {
+ return '';
+ }
+ }
+
+ return iconv_substr($s, $start, $length, $encoding).'';
+ }
+
+ public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
+ {
+ $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
+ $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
+
+ return self::mb_strpos($haystack, $needle, $offset, $encoding);
+ }
+
+ public static function mb_stristr($haystack, $needle, $part = false, $encoding = null)
+ {
+ $pos = self::mb_stripos($haystack, $needle, 0, $encoding);
+
+ return self::getSubpart($pos, $part, $haystack, $encoding);
+ }
+
+ public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null)
+ {
+ $encoding = self::getEncoding($encoding);
+ if ('CP850' === $encoding || 'ASCII' === $encoding) {
+ return strrchr($haystack, $needle, $part);
+ }
+ $needle = self::mb_substr($needle, 0, 1, $encoding);
+ $pos = iconv_strrpos($haystack, $needle, $encoding);
+
+ return self::getSubpart($pos, $part, $haystack, $encoding);
+ }
+
+ public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null)
+ {
+ $needle = self::mb_substr($needle, 0, 1, $encoding);
+ $pos = self::mb_strripos($haystack, $needle, $encoding);
+
+ return self::getSubpart($pos, $part, $haystack, $encoding);
+ }
+
+ public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
+ {
+ $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
+ $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
+
+ return self::mb_strrpos($haystack, $needle, $offset, $encoding);
+ }
+
+ public static function mb_strstr($haystack, $needle, $part = false, $encoding = null)
+ {
+ $pos = strpos($haystack, $needle);
+ if (false === $pos) {
+ return false;
+ }
+ if ($part) {
+ return substr($haystack, 0, $pos);
+ }
+
+ return substr($haystack, $pos);
+ }
+
+ public static function mb_get_info($type = 'all')
+ {
+ $info = array(
+ 'internal_encoding' => self::$internalEncoding,
+ 'http_output' => 'pass',
+ 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)',
+ 'func_overload' => 0,
+ 'func_overload_list' => 'no overload',
+ 'mail_charset' => 'UTF-8',
+ 'mail_header_encoding' => 'BASE64',
+ 'mail_body_encoding' => 'BASE64',
+ 'illegal_chars' => 0,
+ 'encoding_translation' => 'Off',
+ 'language' => self::$language,
+ 'detect_order' => self::$encodingList,
+ 'substitute_character' => 'none',
+ 'strict_detection' => 'Off',
+ );
+
+ if ('all' === $type) {
+ return $info;
+ }
+ if (isset($info[$type])) {
+ return $info[$type];
+ }
+
+ return false;
+ }
+
+ public static function mb_http_input($type = '')
+ {
+ return false;
+ }
+
+ public static function mb_http_output($encoding = null)
+ {
+ return null !== $encoding ? 'pass' === $encoding : 'pass';
+ }
+
+ public static function mb_strwidth($s, $encoding = null)
+ {
+ $encoding = self::getEncoding($encoding);
+
+ if ('UTF-8' !== $encoding) {
+ $s = iconv($encoding, 'UTF-8//IGNORE', $s);
+ }
+
+ $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
+
+ return ($wide << 1) + iconv_strlen($s, 'UTF-8');
+ }
+
+ public static function mb_substr_count($haystack, $needle, $encoding = null)
+ {
+ return substr_count($haystack, $needle);
+ }
+
+ public static function mb_output_handler($contents, $status)
+ {
+ return $contents;
+ }
+
+ public static function mb_chr($code, $encoding = null)
+ {
+ if (0x80 > $code %= 0x200000) {
+ $s = chr($code);
+ } elseif (0x800 > $code) {
+ $s = chr(0xC0 | $code >> 6).chr(0x80 | $code & 0x3F);
+ } elseif (0x10000 > $code) {
+ $s = chr(0xE0 | $code >> 12).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F);
+ } else {
+ $s = chr(0xF0 | $code >> 18).chr(0x80 | $code >> 12 & 0x3F).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F);
+ }
+
+ if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
+ $s = mb_convert_encoding($s, $encoding, 'UTF-8');
+ }
+
+ return $s;
+ }
+
+ public static function mb_ord($s, $encoding = null)
+ {
+ if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
+ $s = mb_convert_encoding($s, 'UTF-8', $encoding);
+ }
+
+ $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
+ if (0xF0 <= $code) {
+ return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
+ }
+ if (0xE0 <= $code) {
+ return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
+ }
+ if (0xC0 <= $code) {
+ return (($code - 0xC0) << 6) + $s[2] - 0x80;
+ }
+
+ return $code;
+ }
+
+ private static function getSubpart($pos, $part, $haystack, $encoding)
+ {
+ if (false === $pos) {
+ return false;
+ }
+ if ($part) {
+ return self::mb_substr($haystack, 0, $pos, $encoding);
+ }
+
+ return self::mb_substr($haystack, $pos, null, $encoding);
+ }
+
+ private static function html_encoding_callback($m)
+ {
+ $i = 1;
+ $entities = '';
+ $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8'));
+
+ while (isset($m[$i])) {
+ if (0x80 > $m[$i]) {
+ $entities .= chr($m[$i++]);
+ continue;
+ }
+ if (0xF0 <= $m[$i]) {
+ $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
+ } elseif (0xE0 <= $m[$i]) {
+ $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
+ } else {
+ $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
+ }
+
+ $entities .= '&#'.$c.';';
+ }
+
+ return $entities;
+ }
+
+ private static function title_case_lower($s)
+ {
+ return self::mb_convert_case($s[0], MB_CASE_LOWER, 'UTF-8');
+ }
+
+ private static function title_case_upper($s)
+ {
+ return self::mb_convert_case($s[0], MB_CASE_UPPER, 'UTF-8');
+ }
+
+ private static function getData($file)
+ {
+ if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
+ return require $file;
+ }
+
+ return false;
+ }
+
+ private static function getEncoding($encoding)
+ {
+ if (null === $encoding) {
+ return self::$internalEncoding;
+ }
+
+ $encoding = strtoupper($encoding);
+
+ if ('8BIT' === $encoding || 'BINARY' === $encoding) {
+ return 'CP850';
+ }
+ if ('UTF8' === $encoding) {
+ return 'UTF-8';
+ }
+
+ return $encoding;
+ }
+}
--- /dev/null
+Symfony Polyfill / Mbstring
+===========================
+
+This component provides a partial, native PHP implementation for the
+[Mbstring](http://php.net/mbstring) extension.
+
+More information can be found in the
+[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
+
+License
+=======
+
+This library is released under the [MIT license](LICENSE).
--- /dev/null
+<?php
+
+static $data = array (
+ 'A' => 'a',
+ 'B' => 'b',
+ 'C' => 'c',
+ 'D' => 'd',
+ 'E' => 'e',
+ 'F' => 'f',
+ 'G' => 'g',
+ 'H' => 'h',
+ 'I' => 'i',
+ 'J' => 'j',
+ 'K' => 'k',
+ 'L' => 'l',
+ 'M' => 'm',
+ 'N' => 'n',
+ 'O' => 'o',
+ 'P' => 'p',
+ 'Q' => 'q',
+ 'R' => 'r',
+ 'S' => 's',
+ 'T' => 't',
+ 'U' => 'u',
+ 'V' => 'v',
+ 'W' => 'w',
+ 'X' => 'x',
+ 'Y' => 'y',
+ 'Z' => 'z',
+ 'À' => 'à',
+ 'Á' => 'á',
+ 'Â' => 'â',
+ 'Ã' => 'ã',
+ 'Ä' => 'ä',
+ 'Å' => 'å',
+ 'Æ' => 'æ',
+ 'Ç' => 'ç',
+ 'È' => 'è',
+ 'É' => 'é',
+ 'Ê' => 'ê',
+ 'Ë' => 'ë',
+ 'Ì' => 'ì',
+ 'Í' => 'í',
+ 'Î' => 'î',
+ 'Ï' => 'ï',
+ 'Ð' => 'ð',
+ 'Ñ' => 'ñ',
+ 'Ò' => 'ò',
+ 'Ó' => 'ó',
+ 'Ô' => 'ô',
+ 'Õ' => 'õ',
+ 'Ö' => 'ö',
+ 'Ø' => 'ø',
+ 'Ù' => 'ù',
+ 'Ú' => 'ú',
+ 'Û' => 'û',
+ 'Ü' => 'ü',
+ 'Ý' => 'ý',
+ 'Þ' => 'þ',
+ 'Ā' => 'ā',
+ 'Ă' => 'ă',
+ 'Ą' => 'ą',
+ 'Ć' => 'ć',
+ 'Ĉ' => 'ĉ',
+ 'Ċ' => 'ċ',
+ 'Č' => 'č',
+ 'Ď' => 'ď',
+ 'Đ' => 'đ',
+ 'Ē' => 'ē',
+ 'Ĕ' => 'ĕ',
+ 'Ė' => 'ė',
+ 'Ę' => 'ę',
+ 'Ě' => 'ě',
+ 'Ĝ' => 'ĝ',
+ 'Ğ' => 'ğ',
+ 'Ġ' => 'ġ',
+ 'Ģ' => 'ģ',
+ 'Ĥ' => 'ĥ',
+ 'Ħ' => 'ħ',
+ 'Ĩ' => 'ĩ',
+ 'Ī' => 'ī',
+ 'Ĭ' => 'ĭ',
+ 'Į' => 'į',
+ 'İ' => 'i',
+ 'IJ' => 'ij',
+ 'Ĵ' => 'ĵ',
+ 'Ķ' => 'ķ',
+ 'Ĺ' => 'ĺ',
+ 'Ļ' => 'ļ',
+ 'Ľ' => 'ľ',
+ 'Ŀ' => 'ŀ',
+ 'Ł' => 'ł',
+ 'Ń' => 'ń',
+ 'Ņ' => 'ņ',
+ 'Ň' => 'ň',
+ 'Ŋ' => 'ŋ',
+ 'Ō' => 'ō',
+ 'Ŏ' => 'ŏ',
+ 'Ő' => 'ő',
+ 'Œ' => 'œ',
+ 'Ŕ' => 'ŕ',
+ 'Ŗ' => 'ŗ',
+ 'Ř' => 'ř',
+ 'Ś' => 'ś',
+ 'Ŝ' => 'ŝ',
+ 'Ş' => 'ş',
+ 'Š' => 'š',
+ 'Ţ' => 'ţ',
+ 'Ť' => 'ť',
+ 'Ŧ' => 'ŧ',
+ 'Ũ' => 'ũ',
+ 'Ū' => 'ū',
+ 'Ŭ' => 'ŭ',
+ 'Ů' => 'ů',
+ 'Ű' => 'ű',
+ 'Ų' => 'ų',
+ 'Ŵ' => 'ŵ',
+ 'Ŷ' => 'ŷ',
+ 'Ÿ' => 'ÿ',
+ 'Ź' => 'ź',
+ 'Ż' => 'ż',
+ 'Ž' => 'ž',
+ 'Ɓ' => 'ɓ',
+ 'Ƃ' => 'ƃ',
+ 'Ƅ' => 'ƅ',
+ 'Ɔ' => 'ɔ',
+ 'Ƈ' => 'ƈ',
+ 'Ɖ' => 'ɖ',
+ 'Ɗ' => 'ɗ',
+ 'Ƌ' => 'ƌ',
+ 'Ǝ' => 'ǝ',
+ 'Ə' => 'ə',
+ 'Ɛ' => 'ɛ',
+ 'Ƒ' => 'ƒ',
+ 'Ɠ' => 'ɠ',
+ 'Ɣ' => 'ɣ',
+ 'Ɩ' => 'ɩ',
+ 'Ɨ' => 'ɨ',
+ 'Ƙ' => 'ƙ',
+ 'Ɯ' => 'ɯ',
+ 'Ɲ' => 'ɲ',
+ 'Ɵ' => 'ɵ',
+ 'Ơ' => 'ơ',
+ 'Ƣ' => 'ƣ',
+ 'Ƥ' => 'ƥ',
+ 'Ʀ' => 'ʀ',
+ 'Ƨ' => 'ƨ',
+ 'Ʃ' => 'ʃ',
+ 'Ƭ' => 'ƭ',
+ 'Ʈ' => 'ʈ',
+ 'Ư' => 'ư',
+ 'Ʊ' => 'ʊ',
+ 'Ʋ' => 'ʋ',
+ 'Ƴ' => 'ƴ',
+ 'Ƶ' => 'ƶ',
+ 'Ʒ' => 'ʒ',
+ 'Ƹ' => 'ƹ',
+ 'Ƽ' => 'ƽ',
+ 'DŽ' => 'dž',
+ 'Dž' => 'dž',
+ 'LJ' => 'lj',
+ 'Lj' => 'lj',
+ 'NJ' => 'nj',
+ 'Nj' => 'nj',
+ 'Ǎ' => 'ǎ',
+ 'Ǐ' => 'ǐ',
+ 'Ǒ' => 'ǒ',
+ 'Ǔ' => 'ǔ',
+ 'Ǖ' => 'ǖ',
+ 'Ǘ' => 'ǘ',
+ 'Ǚ' => 'ǚ',
+ 'Ǜ' => 'ǜ',
+ 'Ǟ' => 'ǟ',
+ 'Ǡ' => 'ǡ',
+ 'Ǣ' => 'ǣ',
+ 'Ǥ' => 'ǥ',
+ 'Ǧ' => 'ǧ',
+ 'Ǩ' => 'ǩ',
+ 'Ǫ' => 'ǫ',
+ 'Ǭ' => 'ǭ',
+ 'Ǯ' => 'ǯ',
+ 'DZ' => 'dz',
+ 'Dz' => 'dz',
+ 'Ǵ' => 'ǵ',
+ 'Ƕ' => 'ƕ',
+ 'Ƿ' => 'ƿ',
+ 'Ǹ' => 'ǹ',
+ 'Ǻ' => 'ǻ',
+ 'Ǽ' => 'ǽ',
+ 'Ǿ' => 'ǿ',
+ 'Ȁ' => 'ȁ',
+ 'Ȃ' => 'ȃ',
+ 'Ȅ' => 'ȅ',
+ 'Ȇ' => 'ȇ',
+ 'Ȉ' => 'ȉ',
+ 'Ȋ' => 'ȋ',
+ 'Ȍ' => 'ȍ',
+ 'Ȏ' => 'ȏ',
+ 'Ȑ' => 'ȑ',
+ 'Ȓ' => 'ȓ',
+ 'Ȕ' => 'ȕ',
+ 'Ȗ' => 'ȗ',
+ 'Ș' => 'ș',
+ 'Ț' => 'ț',
+ 'Ȝ' => 'ȝ',
+ 'Ȟ' => 'ȟ',
+ 'Ƞ' => 'ƞ',
+ 'Ȣ' => 'ȣ',
+ 'Ȥ' => 'ȥ',
+ 'Ȧ' => 'ȧ',
+ 'Ȩ' => 'ȩ',
+ 'Ȫ' => 'ȫ',
+ 'Ȭ' => 'ȭ',
+ 'Ȯ' => 'ȯ',
+ 'Ȱ' => 'ȱ',
+ 'Ȳ' => 'ȳ',
+ 'Ⱥ' => 'ⱥ',
+ 'Ȼ' => 'ȼ',
+ 'Ƚ' => 'ƚ',
+ 'Ⱦ' => 'ⱦ',
+ 'Ɂ' => 'ɂ',
+ 'Ƀ' => 'ƀ',
+ 'Ʉ' => 'ʉ',
+ 'Ʌ' => 'ʌ',
+ 'Ɇ' => 'ɇ',
+ 'Ɉ' => 'ɉ',
+ 'Ɋ' => 'ɋ',
+ 'Ɍ' => 'ɍ',
+ 'Ɏ' => 'ɏ',
+ 'Ͱ' => 'ͱ',
+ 'Ͳ' => 'ͳ',
+ 'Ͷ' => 'ͷ',
+ 'Ϳ' => 'ϳ',
+ 'Ά' => 'ά',
+ 'Έ' => 'έ',
+ 'Ή' => 'ή',
+ 'Ί' => 'ί',
+ 'Ό' => 'ό',
+ 'Ύ' => 'ύ',
+ 'Ώ' => 'ώ',
+ 'Α' => 'α',
+ 'Β' => 'β',
+ 'Γ' => 'γ',
+ 'Δ' => 'δ',
+ 'Ε' => 'ε',
+ 'Ζ' => 'ζ',
+ 'Η' => 'η',
+ 'Θ' => 'θ',
+ 'Ι' => 'ι',
+ 'Κ' => 'κ',
+ 'Λ' => 'λ',
+ 'Μ' => 'μ',
+ 'Ν' => 'ν',
+ 'Ξ' => 'ξ',
+ 'Ο' => 'ο',
+ 'Π' => 'π',
+ 'Ρ' => 'ρ',
+ 'Σ' => 'σ',
+ 'Τ' => 'τ',
+ 'Υ' => 'υ',
+ 'Φ' => 'φ',
+ 'Χ' => 'χ',
+ 'Ψ' => 'ψ',
+ 'Ω' => 'ω',
+ 'Ϊ' => 'ϊ',
+ 'Ϋ' => 'ϋ',
+ 'Ϗ' => 'ϗ',
+ 'Ϙ' => 'ϙ',
+ 'Ϛ' => 'ϛ',
+ 'Ϝ' => 'ϝ',
+ 'Ϟ' => 'ϟ',
+ 'Ϡ' => 'ϡ',
+ 'Ϣ' => 'ϣ',
+ 'Ϥ' => 'ϥ',
+ 'Ϧ' => 'ϧ',
+ 'Ϩ' => 'ϩ',
+ 'Ϫ' => 'ϫ',
+ 'Ϭ' => 'ϭ',
+ 'Ϯ' => 'ϯ',
+ 'ϴ' => 'θ',
+ 'Ϸ' => 'ϸ',
+ 'Ϲ' => 'ϲ',
+ 'Ϻ' => 'ϻ',
+ 'Ͻ' => 'ͻ',
+ 'Ͼ' => 'ͼ',
+ 'Ͽ' => 'ͽ',
+ 'Ѐ' => 'ѐ',
+ 'Ё' => 'ё',
+ 'Ђ' => 'ђ',
+ 'Ѓ' => 'ѓ',
+ 'Є' => 'є',
+ 'Ѕ' => 'ѕ',
+ 'І' => 'і',
+ 'Ї' => 'ї',
+ 'Ј' => 'ј',
+ 'Љ' => 'љ',
+ 'Њ' => 'њ',
+ 'Ћ' => 'ћ',
+ 'Ќ' => 'ќ',
+ 'Ѝ' => 'ѝ',
+ 'Ў' => 'ў',
+ 'Џ' => 'џ',
+ 'А' => 'а',
+ 'Б' => 'б',
+ 'В' => 'в',
+ 'Г' => 'г',
+ 'Д' => 'д',
+ 'Е' => 'е',
+ 'Ж' => 'ж',
+ 'З' => 'з',
+ 'И' => 'и',
+ 'Й' => 'й',
+ 'К' => 'к',
+ 'Л' => 'л',
+ 'М' => 'м',
+ 'Н' => 'н',
+ 'О' => 'о',
+ 'П' => 'п',
+ 'Р' => 'р',
+ 'С' => 'с',
+ 'Т' => 'т',
+ 'У' => 'у',
+ 'Ф' => 'ф',
+ 'Х' => 'х',
+ 'Ц' => 'ц',
+ 'Ч' => 'ч',
+ 'Ш' => 'ш',
+ 'Щ' => 'щ',
+ 'Ъ' => 'ъ',
+ 'Ы' => 'ы',
+ 'Ь' => 'ь',
+ 'Э' => 'э',
+ 'Ю' => 'ю',
+ 'Я' => 'я',
+ 'Ѡ' => 'ѡ',
+ 'Ѣ' => 'ѣ',
+ 'Ѥ' => 'ѥ',
+ 'Ѧ' => 'ѧ',
+ 'Ѩ' => 'ѩ',
+ 'Ѫ' => 'ѫ',
+ 'Ѭ' => 'ѭ',
+ 'Ѯ' => 'ѯ',
+ 'Ѱ' => 'ѱ',
+ 'Ѳ' => 'ѳ',
+ 'Ѵ' => 'ѵ',
+ 'Ѷ' => 'ѷ',
+ 'Ѹ' => 'ѹ',
+ 'Ѻ' => 'ѻ',
+ 'Ѽ' => 'ѽ',
+ 'Ѿ' => 'ѿ',
+ 'Ҁ' => 'ҁ',
+ 'Ҋ' => 'ҋ',
+ 'Ҍ' => 'ҍ',
+ 'Ҏ' => 'ҏ',
+ 'Ґ' => 'ґ',
+ 'Ғ' => 'ғ',
+ 'Ҕ' => 'ҕ',
+ 'Җ' => 'җ',
+ 'Ҙ' => 'ҙ',
+ 'Қ' => 'қ',
+ 'Ҝ' => 'ҝ',
+ 'Ҟ' => 'ҟ',
+ 'Ҡ' => 'ҡ',
+ 'Ң' => 'ң',
+ 'Ҥ' => 'ҥ',
+ 'Ҧ' => 'ҧ',
+ 'Ҩ' => 'ҩ',
+ 'Ҫ' => 'ҫ',
+ 'Ҭ' => 'ҭ',
+ 'Ү' => 'ү',
+ 'Ұ' => 'ұ',
+ 'Ҳ' => 'ҳ',
+ 'Ҵ' => 'ҵ',
+ 'Ҷ' => 'ҷ',
+ 'Ҹ' => 'ҹ',
+ 'Һ' => 'һ',
+ 'Ҽ' => 'ҽ',
+ 'Ҿ' => 'ҿ',
+ 'Ӏ' => 'ӏ',
+ 'Ӂ' => 'ӂ',
+ 'Ӄ' => 'ӄ',
+ 'Ӆ' => 'ӆ',
+ 'Ӈ' => 'ӈ',
+ 'Ӊ' => 'ӊ',
+ 'Ӌ' => 'ӌ',
+ 'Ӎ' => 'ӎ',
+ 'Ӑ' => 'ӑ',
+ 'Ӓ' => 'ӓ',
+ 'Ӕ' => 'ӕ',
+ 'Ӗ' => 'ӗ',
+ 'Ә' => 'ә',
+ 'Ӛ' => 'ӛ',
+ 'Ӝ' => 'ӝ',
+ 'Ӟ' => 'ӟ',
+ 'Ӡ' => 'ӡ',
+ 'Ӣ' => 'ӣ',
+ 'Ӥ' => 'ӥ',
+ 'Ӧ' => 'ӧ',
+ 'Ө' => 'ө',
+ 'Ӫ' => 'ӫ',
+ 'Ӭ' => 'ӭ',
+ 'Ӯ' => 'ӯ',
+ 'Ӱ' => 'ӱ',
+ 'Ӳ' => 'ӳ',
+ 'Ӵ' => 'ӵ',
+ 'Ӷ' => 'ӷ',
+ 'Ӹ' => 'ӹ',
+ 'Ӻ' => 'ӻ',
+ 'Ӽ' => 'ӽ',
+ 'Ӿ' => 'ӿ',
+ 'Ԁ' => 'ԁ',
+ 'Ԃ' => 'ԃ',
+ 'Ԅ' => 'ԅ',
+ 'Ԇ' => 'ԇ',
+ 'Ԉ' => 'ԉ',
+ 'Ԋ' => 'ԋ',
+ 'Ԍ' => 'ԍ',
+ 'Ԏ' => 'ԏ',
+ 'Ԑ' => 'ԑ',
+ 'Ԓ' => 'ԓ',
+ 'Ԕ' => 'ԕ',
+ 'Ԗ' => 'ԗ',
+ 'Ԙ' => 'ԙ',
+ 'Ԛ' => 'ԛ',
+ 'Ԝ' => 'ԝ',
+ 'Ԟ' => 'ԟ',
+ 'Ԡ' => 'ԡ',
+ 'Ԣ' => 'ԣ',
+ 'Ԥ' => 'ԥ',
+ 'Ԧ' => 'ԧ',
+ 'Ԩ' => 'ԩ',
+ 'Ԫ' => 'ԫ',
+ 'Ԭ' => 'ԭ',
+ 'Ԯ' => 'ԯ',
+ 'Ա' => 'ա',
+ 'Բ' => 'բ',
+ 'Գ' => 'գ',
+ 'Դ' => 'դ',
+ 'Ե' => 'ե',
+ 'Զ' => 'զ',
+ 'Է' => 'է',
+ 'Ը' => 'ը',
+ 'Թ' => 'թ',
+ 'Ժ' => 'ժ',
+ 'Ի' => 'ի',
+ 'Լ' => 'լ',
+ 'Խ' => 'խ',
+ 'Ծ' => 'ծ',
+ 'Կ' => 'կ',
+ 'Հ' => 'հ',
+ 'Ձ' => 'ձ',
+ 'Ղ' => 'ղ',
+ 'Ճ' => 'ճ',
+ 'Մ' => 'մ',
+ 'Յ' => 'յ',
+ 'Ն' => 'ն',
+ 'Շ' => 'շ',
+ 'Ո' => 'ո',
+ 'Չ' => 'չ',
+ 'Պ' => 'պ',
+ 'Ջ' => 'ջ',
+ 'Ռ' => 'ռ',
+ 'Ս' => 'ս',
+ 'Վ' => 'վ',
+ 'Տ' => 'տ',
+ 'Ր' => 'ր',
+ 'Ց' => 'ց',
+ 'Ւ' => 'ւ',
+ 'Փ' => 'փ',
+ 'Ք' => 'ք',
+ 'Օ' => 'օ',
+ 'Ֆ' => 'ֆ',
+ 'Ⴀ' => 'ⴀ',
+ 'Ⴁ' => 'ⴁ',
+ 'Ⴂ' => 'ⴂ',
+ 'Ⴃ' => 'ⴃ',
+ 'Ⴄ' => 'ⴄ',
+ 'Ⴅ' => 'ⴅ',
+ 'Ⴆ' => 'ⴆ',
+ 'Ⴇ' => 'ⴇ',
+ 'Ⴈ' => 'ⴈ',
+ 'Ⴉ' => 'ⴉ',
+ 'Ⴊ' => 'ⴊ',
+ 'Ⴋ' => 'ⴋ',
+ 'Ⴌ' => 'ⴌ',
+ 'Ⴍ' => 'ⴍ',
+ 'Ⴎ' => 'ⴎ',
+ 'Ⴏ' => 'ⴏ',
+ 'Ⴐ' => 'ⴐ',
+ 'Ⴑ' => 'ⴑ',
+ 'Ⴒ' => 'ⴒ',
+ 'Ⴓ' => 'ⴓ',
+ 'Ⴔ' => 'ⴔ',
+ 'Ⴕ' => 'ⴕ',
+ 'Ⴖ' => 'ⴖ',
+ 'Ⴗ' => 'ⴗ',
+ 'Ⴘ' => 'ⴘ',
+ 'Ⴙ' => 'ⴙ',
+ 'Ⴚ' => 'ⴚ',
+ 'Ⴛ' => 'ⴛ',
+ 'Ⴜ' => 'ⴜ',
+ 'Ⴝ' => 'ⴝ',
+ 'Ⴞ' => 'ⴞ',
+ 'Ⴟ' => 'ⴟ',
+ 'Ⴠ' => 'ⴠ',
+ 'Ⴡ' => 'ⴡ',
+ 'Ⴢ' => 'ⴢ',
+ 'Ⴣ' => 'ⴣ',
+ 'Ⴤ' => 'ⴤ',
+ 'Ⴥ' => 'ⴥ',
+ 'Ⴧ' => 'ⴧ',
+ 'Ⴭ' => 'ⴭ',
+ 'Ḁ' => 'ḁ',
+ 'Ḃ' => 'ḃ',
+ 'Ḅ' => 'ḅ',
+ 'Ḇ' => 'ḇ',
+ 'Ḉ' => 'ḉ',
+ 'Ḋ' => 'ḋ',
+ 'Ḍ' => 'ḍ',
+ 'Ḏ' => 'ḏ',
+ 'Ḑ' => 'ḑ',
+ 'Ḓ' => 'ḓ',
+ 'Ḕ' => 'ḕ',
+ 'Ḗ' => 'ḗ',
+ 'Ḙ' => 'ḙ',
+ 'Ḛ' => 'ḛ',
+ 'Ḝ' => 'ḝ',
+ 'Ḟ' => 'ḟ',
+ 'Ḡ' => 'ḡ',
+ 'Ḣ' => 'ḣ',
+ 'Ḥ' => 'ḥ',
+ 'Ḧ' => 'ḧ',
+ 'Ḩ' => 'ḩ',
+ 'Ḫ' => 'ḫ',
+ 'Ḭ' => 'ḭ',
+ 'Ḯ' => 'ḯ',
+ 'Ḱ' => 'ḱ',
+ 'Ḳ' => 'ḳ',
+ 'Ḵ' => 'ḵ',
+ 'Ḷ' => 'ḷ',
+ 'Ḹ' => 'ḹ',
+ 'Ḻ' => 'ḻ',
+ 'Ḽ' => 'ḽ',
+ 'Ḿ' => 'ḿ',
+ 'Ṁ' => 'ṁ',
+ 'Ṃ' => 'ṃ',
+ 'Ṅ' => 'ṅ',
+ 'Ṇ' => 'ṇ',
+ 'Ṉ' => 'ṉ',
+ 'Ṋ' => 'ṋ',
+ 'Ṍ' => 'ṍ',
+ 'Ṏ' => 'ṏ',
+ 'Ṑ' => 'ṑ',
+ 'Ṓ' => 'ṓ',
+ 'Ṕ' => 'ṕ',
+ 'Ṗ' => 'ṗ',
+ 'Ṙ' => 'ṙ',
+ 'Ṛ' => 'ṛ',
+ 'Ṝ' => 'ṝ',
+ 'Ṟ' => 'ṟ',
+ 'Ṡ' => 'ṡ',
+ 'Ṣ' => 'ṣ',
+ 'Ṥ' => 'ṥ',
+ 'Ṧ' => 'ṧ',
+ 'Ṩ' => 'ṩ',
+ 'Ṫ' => 'ṫ',
+ 'Ṭ' => 'ṭ',
+ 'Ṯ' => 'ṯ',
+ 'Ṱ' => 'ṱ',
+ 'Ṳ' => 'ṳ',
+ 'Ṵ' => 'ṵ',
+ 'Ṷ' => 'ṷ',
+ 'Ṹ' => 'ṹ',
+ 'Ṻ' => 'ṻ',
+ 'Ṽ' => 'ṽ',
+ 'Ṿ' => 'ṿ',
+ 'Ẁ' => 'ẁ',
+ 'Ẃ' => 'ẃ',
+ 'Ẅ' => 'ẅ',
+ 'Ẇ' => 'ẇ',
+ 'Ẉ' => 'ẉ',
+ 'Ẋ' => 'ẋ',
+ 'Ẍ' => 'ẍ',
+ 'Ẏ' => 'ẏ',
+ 'Ẑ' => 'ẑ',
+ 'Ẓ' => 'ẓ',
+ 'Ẕ' => 'ẕ',
+ 'ẞ' => 'ß',
+ 'Ạ' => 'ạ',
+ 'Ả' => 'ả',
+ 'Ấ' => 'ấ',
+ 'Ầ' => 'ầ',
+ 'Ẩ' => 'ẩ',
+ 'Ẫ' => 'ẫ',
+ 'Ậ' => 'ậ',
+ 'Ắ' => 'ắ',
+ 'Ằ' => 'ằ',
+ 'Ẳ' => 'ẳ',
+ 'Ẵ' => 'ẵ',
+ 'Ặ' => 'ặ',
+ 'Ẹ' => 'ẹ',
+ 'Ẻ' => 'ẻ',
+ 'Ẽ' => 'ẽ',
+ 'Ế' => 'ế',
+ 'Ề' => 'ề',
+ 'Ể' => 'ể',
+ 'Ễ' => 'ễ',
+ 'Ệ' => 'ệ',
+ 'Ỉ' => 'ỉ',
+ 'Ị' => 'ị',
+ 'Ọ' => 'ọ',
+ 'Ỏ' => 'ỏ',
+ 'Ố' => 'ố',
+ 'Ồ' => 'ồ',
+ 'Ổ' => 'ổ',
+ 'Ỗ' => 'ỗ',
+ 'Ộ' => 'ộ',
+ 'Ớ' => 'ớ',
+ 'Ờ' => 'ờ',
+ 'Ở' => 'ở',
+ 'Ỡ' => 'ỡ',
+ 'Ợ' => 'ợ',
+ 'Ụ' => 'ụ',
+ 'Ủ' => 'ủ',
+ 'Ứ' => 'ứ',
+ 'Ừ' => 'ừ',
+ 'Ử' => 'ử',
+ 'Ữ' => 'ữ',
+ 'Ự' => 'ự',
+ 'Ỳ' => 'ỳ',
+ 'Ỵ' => 'ỵ',
+ 'Ỷ' => 'ỷ',
+ 'Ỹ' => 'ỹ',
+ 'Ỻ' => 'ỻ',
+ 'Ỽ' => 'ỽ',
+ 'Ỿ' => 'ỿ',
+ 'Ἀ' => 'ἀ',
+ 'Ἁ' => 'ἁ',
+ 'Ἂ' => 'ἂ',
+ 'Ἃ' => 'ἃ',
+ 'Ἄ' => 'ἄ',
+ 'Ἅ' => 'ἅ',
+ 'Ἆ' => 'ἆ',
+ 'Ἇ' => 'ἇ',
+ 'Ἐ' => 'ἐ',
+ 'Ἑ' => 'ἑ',
+ 'Ἒ' => 'ἒ',
+ 'Ἓ' => 'ἓ',
+ 'Ἔ' => 'ἔ',
+ 'Ἕ' => 'ἕ',
+ 'Ἠ' => 'ἠ',
+ 'Ἡ' => 'ἡ',
+ 'Ἢ' => 'ἢ',
+ 'Ἣ' => 'ἣ',
+ 'Ἤ' => 'ἤ',
+ 'Ἥ' => 'ἥ',
+ 'Ἦ' => 'ἦ',
+ 'Ἧ' => 'ἧ',
+ 'Ἰ' => 'ἰ',
+ 'Ἱ' => 'ἱ',
+ 'Ἲ' => 'ἲ',
+ 'Ἳ' => 'ἳ',
+ 'Ἴ' => 'ἴ',
+ 'Ἵ' => 'ἵ',
+ 'Ἶ' => 'ἶ',
+ 'Ἷ' => 'ἷ',
+ 'Ὀ' => 'ὀ',
+ 'Ὁ' => 'ὁ',
+ 'Ὂ' => 'ὂ',
+ 'Ὃ' => 'ὃ',
+ 'Ὄ' => 'ὄ',
+ 'Ὅ' => 'ὅ',
+ 'Ὑ' => 'ὑ',
+ 'Ὓ' => 'ὓ',
+ 'Ὕ' => 'ὕ',
+ 'Ὗ' => 'ὗ',
+ 'Ὠ' => 'ὠ',
+ 'Ὡ' => 'ὡ',
+ 'Ὢ' => 'ὢ',
+ 'Ὣ' => 'ὣ',
+ 'Ὤ' => 'ὤ',
+ 'Ὥ' => 'ὥ',
+ 'Ὦ' => 'ὦ',
+ 'Ὧ' => 'ὧ',
+ 'ᾈ' => 'ᾀ',
+ 'ᾉ' => 'ᾁ',
+ 'ᾊ' => 'ᾂ',
+ 'ᾋ' => 'ᾃ',
+ 'ᾌ' => 'ᾄ',
+ 'ᾍ' => 'ᾅ',
+ 'ᾎ' => 'ᾆ',
+ 'ᾏ' => 'ᾇ',
+ 'ᾘ' => 'ᾐ',
+ 'ᾙ' => 'ᾑ',
+ 'ᾚ' => 'ᾒ',
+ 'ᾛ' => 'ᾓ',
+ 'ᾜ' => 'ᾔ',
+ 'ᾝ' => 'ᾕ',
+ 'ᾞ' => 'ᾖ',
+ 'ᾟ' => 'ᾗ',
+ 'ᾨ' => 'ᾠ',
+ 'ᾩ' => 'ᾡ',
+ 'ᾪ' => 'ᾢ',
+ 'ᾫ' => 'ᾣ',
+ 'ᾬ' => 'ᾤ',
+ 'ᾭ' => 'ᾥ',
+ 'ᾮ' => 'ᾦ',
+ 'ᾯ' => 'ᾧ',
+ 'Ᾰ' => 'ᾰ',
+ 'Ᾱ' => 'ᾱ',
+ 'Ὰ' => 'ὰ',
+ 'Ά' => 'ά',
+ 'ᾼ' => 'ᾳ',
+ 'Ὲ' => 'ὲ',
+ 'Έ' => 'έ',
+ 'Ὴ' => 'ὴ',
+ 'Ή' => 'ή',
+ 'ῌ' => 'ῃ',
+ 'Ῐ' => 'ῐ',
+ 'Ῑ' => 'ῑ',
+ 'Ὶ' => 'ὶ',
+ 'Ί' => 'ί',
+ 'Ῠ' => 'ῠ',
+ 'Ῡ' => 'ῡ',
+ 'Ὺ' => 'ὺ',
+ 'Ύ' => 'ύ',
+ 'Ῥ' => 'ῥ',
+ 'Ὸ' => 'ὸ',
+ 'Ό' => 'ό',
+ 'Ὼ' => 'ὼ',
+ 'Ώ' => 'ώ',
+ 'ῼ' => 'ῳ',
+ 'Ω' => 'ω',
+ 'K' => 'k',
+ 'Å' => 'å',
+ 'Ⅎ' => 'ⅎ',
+ 'Ⅰ' => 'ⅰ',
+ 'Ⅱ' => 'ⅱ',
+ 'Ⅲ' => 'ⅲ',
+ 'Ⅳ' => 'ⅳ',
+ 'Ⅴ' => 'ⅴ',
+ 'Ⅵ' => 'ⅵ',
+ 'Ⅶ' => 'ⅶ',
+ 'Ⅷ' => 'ⅷ',
+ 'Ⅸ' => 'ⅸ',
+ 'Ⅹ' => 'ⅹ',
+ 'Ⅺ' => 'ⅺ',
+ 'Ⅻ' => 'ⅻ',
+ 'Ⅼ' => 'ⅼ',
+ 'Ⅽ' => 'ⅽ',
+ 'Ⅾ' => 'ⅾ',
+ 'Ⅿ' => 'ⅿ',
+ 'Ↄ' => 'ↄ',
+ 'Ⓐ' => 'ⓐ',
+ 'Ⓑ' => 'ⓑ',
+ 'Ⓒ' => 'ⓒ',
+ 'Ⓓ' => 'ⓓ',
+ 'Ⓔ' => 'ⓔ',
+ 'Ⓕ' => 'ⓕ',
+ 'Ⓖ' => 'ⓖ',
+ 'Ⓗ' => 'ⓗ',
+ 'Ⓘ' => 'ⓘ',
+ 'Ⓙ' => 'ⓙ',
+ 'Ⓚ' => 'ⓚ',
+ 'Ⓛ' => 'ⓛ',
+ 'Ⓜ' => 'ⓜ',
+ 'Ⓝ' => 'ⓝ',
+ 'Ⓞ' => 'ⓞ',
+ 'Ⓟ' => 'ⓟ',
+ 'Ⓠ' => 'ⓠ',
+ 'Ⓡ' => 'ⓡ',
+ 'Ⓢ' => 'ⓢ',
+ 'Ⓣ' => 'ⓣ',
+ 'Ⓤ' => 'ⓤ',
+ 'Ⓥ' => 'ⓥ',
+ 'Ⓦ' => 'ⓦ',
+ 'Ⓧ' => 'ⓧ',
+ 'Ⓨ' => 'ⓨ',
+ 'Ⓩ' => 'ⓩ',
+ 'Ⰰ' => 'ⰰ',
+ 'Ⰱ' => 'ⰱ',
+ 'Ⰲ' => 'ⰲ',
+ 'Ⰳ' => 'ⰳ',
+ 'Ⰴ' => 'ⰴ',
+ 'Ⰵ' => 'ⰵ',
+ 'Ⰶ' => 'ⰶ',
+ 'Ⰷ' => 'ⰷ',
+ 'Ⰸ' => 'ⰸ',
+ 'Ⰹ' => 'ⰹ',
+ 'Ⰺ' => 'ⰺ',
+ 'Ⰻ' => 'ⰻ',
+ 'Ⰼ' => 'ⰼ',
+ 'Ⰽ' => 'ⰽ',
+ 'Ⰾ' => 'ⰾ',
+ 'Ⰿ' => 'ⰿ',
+ 'Ⱀ' => 'ⱀ',
+ 'Ⱁ' => 'ⱁ',
+ 'Ⱂ' => 'ⱂ',
+ 'Ⱃ' => 'ⱃ',
+ 'Ⱄ' => 'ⱄ',
+ 'Ⱅ' => 'ⱅ',
+ 'Ⱆ' => 'ⱆ',
+ 'Ⱇ' => 'ⱇ',
+ 'Ⱈ' => 'ⱈ',
+ 'Ⱉ' => 'ⱉ',
+ 'Ⱊ' => 'ⱊ',
+ 'Ⱋ' => 'ⱋ',
+ 'Ⱌ' => 'ⱌ',
+ 'Ⱍ' => 'ⱍ',
+ 'Ⱎ' => 'ⱎ',
+ 'Ⱏ' => 'ⱏ',
+ 'Ⱐ' => 'ⱐ',
+ 'Ⱑ' => 'ⱑ',
+ 'Ⱒ' => 'ⱒ',
+ 'Ⱓ' => 'ⱓ',
+ 'Ⱔ' => 'ⱔ',
+ 'Ⱕ' => 'ⱕ',
+ 'Ⱖ' => 'ⱖ',
+ 'Ⱗ' => 'ⱗ',
+ 'Ⱘ' => 'ⱘ',
+ 'Ⱙ' => 'ⱙ',
+ 'Ⱚ' => 'ⱚ',
+ 'Ⱛ' => 'ⱛ',
+ 'Ⱜ' => 'ⱜ',
+ 'Ⱝ' => 'ⱝ',
+ 'Ⱞ' => 'ⱞ',
+ 'Ⱡ' => 'ⱡ',
+ 'Ɫ' => 'ɫ',
+ 'Ᵽ' => 'ᵽ',
+ 'Ɽ' => 'ɽ',
+ 'Ⱨ' => 'ⱨ',
+ 'Ⱪ' => 'ⱪ',
+ 'Ⱬ' => 'ⱬ',
+ 'Ɑ' => 'ɑ',
+ 'Ɱ' => 'ɱ',
+ 'Ɐ' => 'ɐ',
+ 'Ɒ' => 'ɒ',
+ 'Ⱳ' => 'ⱳ',
+ 'Ⱶ' => 'ⱶ',
+ 'Ȿ' => 'ȿ',
+ 'Ɀ' => 'ɀ',
+ 'Ⲁ' => 'ⲁ',
+ 'Ⲃ' => 'ⲃ',
+ 'Ⲅ' => 'ⲅ',
+ 'Ⲇ' => 'ⲇ',
+ 'Ⲉ' => 'ⲉ',
+ 'Ⲋ' => 'ⲋ',
+ 'Ⲍ' => 'ⲍ',
+ 'Ⲏ' => 'ⲏ',
+ 'Ⲑ' => 'ⲑ',
+ 'Ⲓ' => 'ⲓ',
+ 'Ⲕ' => 'ⲕ',
+ 'Ⲗ' => 'ⲗ',
+ 'Ⲙ' => 'ⲙ',
+ 'Ⲛ' => 'ⲛ',
+ 'Ⲝ' => 'ⲝ',
+ 'Ⲟ' => 'ⲟ',
+ 'Ⲡ' => 'ⲡ',
+ 'Ⲣ' => 'ⲣ',
+ 'Ⲥ' => 'ⲥ',
+ 'Ⲧ' => 'ⲧ',
+ 'Ⲩ' => 'ⲩ',
+ 'Ⲫ' => 'ⲫ',
+ 'Ⲭ' => 'ⲭ',
+ 'Ⲯ' => 'ⲯ',
+ 'Ⲱ' => 'ⲱ',
+ 'Ⲳ' => 'ⲳ',
+ 'Ⲵ' => 'ⲵ',
+ 'Ⲷ' => 'ⲷ',
+ 'Ⲹ' => 'ⲹ',
+ 'Ⲻ' => 'ⲻ',
+ 'Ⲽ' => 'ⲽ',
+ 'Ⲿ' => 'ⲿ',
+ 'Ⳁ' => 'ⳁ',
+ 'Ⳃ' => 'ⳃ',
+ 'Ⳅ' => 'ⳅ',
+ 'Ⳇ' => 'ⳇ',
+ 'Ⳉ' => 'ⳉ',
+ 'Ⳋ' => 'ⳋ',
+ 'Ⳍ' => 'ⳍ',
+ 'Ⳏ' => 'ⳏ',
+ 'Ⳑ' => 'ⳑ',
+ 'Ⳓ' => 'ⳓ',
+ 'Ⳕ' => 'ⳕ',
+ 'Ⳗ' => 'ⳗ',
+ 'Ⳙ' => 'ⳙ',
+ 'Ⳛ' => 'ⳛ',
+ 'Ⳝ' => 'ⳝ',
+ 'Ⳟ' => 'ⳟ',
+ 'Ⳡ' => 'ⳡ',
+ 'Ⳣ' => 'ⳣ',
+ 'Ⳬ' => 'ⳬ',
+ 'Ⳮ' => 'ⳮ',
+ 'Ⳳ' => 'ⳳ',
+ 'Ꙁ' => 'ꙁ',
+ 'Ꙃ' => 'ꙃ',
+ 'Ꙅ' => 'ꙅ',
+ 'Ꙇ' => 'ꙇ',
+ 'Ꙉ' => 'ꙉ',
+ 'Ꙋ' => 'ꙋ',
+ 'Ꙍ' => 'ꙍ',
+ 'Ꙏ' => 'ꙏ',
+ 'Ꙑ' => 'ꙑ',
+ 'Ꙓ' => 'ꙓ',
+ 'Ꙕ' => 'ꙕ',
+ 'Ꙗ' => 'ꙗ',
+ 'Ꙙ' => 'ꙙ',
+ 'Ꙛ' => 'ꙛ',
+ 'Ꙝ' => 'ꙝ',
+ 'Ꙟ' => 'ꙟ',
+ 'Ꙡ' => 'ꙡ',
+ 'Ꙣ' => 'ꙣ',
+ 'Ꙥ' => 'ꙥ',
+ 'Ꙧ' => 'ꙧ',
+ 'Ꙩ' => 'ꙩ',
+ 'Ꙫ' => 'ꙫ',
+ 'Ꙭ' => 'ꙭ',
+ 'Ꚁ' => 'ꚁ',
+ 'Ꚃ' => 'ꚃ',
+ 'Ꚅ' => 'ꚅ',
+ 'Ꚇ' => 'ꚇ',
+ 'Ꚉ' => 'ꚉ',
+ 'Ꚋ' => 'ꚋ',
+ 'Ꚍ' => 'ꚍ',
+ 'Ꚏ' => 'ꚏ',
+ 'Ꚑ' => 'ꚑ',
+ 'Ꚓ' => 'ꚓ',
+ 'Ꚕ' => 'ꚕ',
+ 'Ꚗ' => 'ꚗ',
+ 'Ꚙ' => 'ꚙ',
+ 'Ꚛ' => 'ꚛ',
+ 'Ꜣ' => 'ꜣ',
+ 'Ꜥ' => 'ꜥ',
+ 'Ꜧ' => 'ꜧ',
+ 'Ꜩ' => 'ꜩ',
+ 'Ꜫ' => 'ꜫ',
+ 'Ꜭ' => 'ꜭ',
+ 'Ꜯ' => 'ꜯ',
+ 'Ꜳ' => 'ꜳ',
+ 'Ꜵ' => 'ꜵ',
+ 'Ꜷ' => 'ꜷ',
+ 'Ꜹ' => 'ꜹ',
+ 'Ꜻ' => 'ꜻ',
+ 'Ꜽ' => 'ꜽ',
+ 'Ꜿ' => 'ꜿ',
+ 'Ꝁ' => 'ꝁ',
+ 'Ꝃ' => 'ꝃ',
+ 'Ꝅ' => 'ꝅ',
+ 'Ꝇ' => 'ꝇ',
+ 'Ꝉ' => 'ꝉ',
+ 'Ꝋ' => 'ꝋ',
+ 'Ꝍ' => 'ꝍ',
+ 'Ꝏ' => 'ꝏ',
+ 'Ꝑ' => 'ꝑ',
+ 'Ꝓ' => 'ꝓ',
+ 'Ꝕ' => 'ꝕ',
+ 'Ꝗ' => 'ꝗ',
+ 'Ꝙ' => 'ꝙ',
+ 'Ꝛ' => 'ꝛ',
+ 'Ꝝ' => 'ꝝ',
+ 'Ꝟ' => 'ꝟ',
+ 'Ꝡ' => 'ꝡ',
+ 'Ꝣ' => 'ꝣ',
+ 'Ꝥ' => 'ꝥ',
+ 'Ꝧ' => 'ꝧ',
+ 'Ꝩ' => 'ꝩ',
+ 'Ꝫ' => 'ꝫ',
+ 'Ꝭ' => 'ꝭ',
+ 'Ꝯ' => 'ꝯ',
+ 'Ꝺ' => 'ꝺ',
+ 'Ꝼ' => 'ꝼ',
+ 'Ᵹ' => 'ᵹ',
+ 'Ꝿ' => 'ꝿ',
+ 'Ꞁ' => 'ꞁ',
+ 'Ꞃ' => 'ꞃ',
+ 'Ꞅ' => 'ꞅ',
+ 'Ꞇ' => 'ꞇ',
+ 'Ꞌ' => 'ꞌ',
+ 'Ɥ' => 'ɥ',
+ 'Ꞑ' => 'ꞑ',
+ 'Ꞓ' => 'ꞓ',
+ 'Ꞗ' => 'ꞗ',
+ 'Ꞙ' => 'ꞙ',
+ 'Ꞛ' => 'ꞛ',
+ 'Ꞝ' => 'ꞝ',
+ 'Ꞟ' => 'ꞟ',
+ 'Ꞡ' => 'ꞡ',
+ 'Ꞣ' => 'ꞣ',
+ 'Ꞥ' => 'ꞥ',
+ 'Ꞧ' => 'ꞧ',
+ 'Ꞩ' => 'ꞩ',
+ 'Ɦ' => 'ɦ',
+ 'Ɜ' => 'ɜ',
+ 'Ɡ' => 'ɡ',
+ 'Ɬ' => 'ɬ',
+ 'Ʞ' => 'ʞ',
+ 'Ʇ' => 'ʇ',
+ 'A' => 'a',
+ 'B' => 'b',
+ 'C' => 'c',
+ 'D' => 'd',
+ 'E' => 'e',
+ 'F' => 'f',
+ 'G' => 'g',
+ 'H' => 'h',
+ 'I' => 'i',
+ 'J' => 'j',
+ 'K' => 'k',
+ 'L' => 'l',
+ 'M' => 'm',
+ 'N' => 'n',
+ 'O' => 'o',
+ 'P' => 'p',
+ 'Q' => 'q',
+ 'R' => 'r',
+ 'S' => 's',
+ 'T' => 't',
+ 'U' => 'u',
+ 'V' => 'v',
+ 'W' => 'w',
+ 'X' => 'x',
+ 'Y' => 'y',
+ 'Z' => 'z',
+ '𐐀' => '𐐨',
+ '𐐁' => '𐐩',
+ '𐐂' => '𐐪',
+ '𐐃' => '𐐫',
+ '𐐄' => '𐐬',
+ '𐐅' => '𐐭',
+ '𐐆' => '𐐮',
+ '𐐇' => '𐐯',
+ '𐐈' => '𐐰',
+ '𐐉' => '𐐱',
+ '𐐊' => '𐐲',
+ '𐐋' => '𐐳',
+ '𐐌' => '𐐴',
+ '𐐍' => '𐐵',
+ '𐐎' => '𐐶',
+ '𐐏' => '𐐷',
+ '𐐐' => '𐐸',
+ '𐐑' => '𐐹',
+ '𐐒' => '𐐺',
+ '𐐓' => '𐐻',
+ '𐐔' => '𐐼',
+ '𐐕' => '𐐽',
+ '𐐖' => '𐐾',
+ '𐐗' => '𐐿',
+ '𐐘' => '𐑀',
+ '𐐙' => '𐑁',
+ '𐐚' => '𐑂',
+ '𐐛' => '𐑃',
+ '𐐜' => '𐑄',
+ '𐐝' => '𐑅',
+ '𐐞' => '𐑆',
+ '𐐟' => '𐑇',
+ '𐐠' => '𐑈',
+ '𐐡' => '𐑉',
+ '𐐢' => '𐑊',
+ '𐐣' => '𐑋',
+ '𐐤' => '𐑌',
+ '𐐥' => '𐑍',
+ '𐐦' => '𐑎',
+ '𐐧' => '𐑏',
+ '𑢠' => '𑣀',
+ '𑢡' => '𑣁',
+ '𑢢' => '𑣂',
+ '𑢣' => '𑣃',
+ '𑢤' => '𑣄',
+ '𑢥' => '𑣅',
+ '𑢦' => '𑣆',
+ '𑢧' => '𑣇',
+ '𑢨' => '𑣈',
+ '𑢩' => '𑣉',
+ '𑢪' => '𑣊',
+ '𑢫' => '𑣋',
+ '𑢬' => '𑣌',
+ '𑢭' => '𑣍',
+ '𑢮' => '𑣎',
+ '𑢯' => '𑣏',
+ '𑢰' => '𑣐',
+ '𑢱' => '𑣑',
+ '𑢲' => '𑣒',
+ '𑢳' => '𑣓',
+ '𑢴' => '𑣔',
+ '𑢵' => '𑣕',
+ '𑢶' => '𑣖',
+ '𑢷' => '𑣗',
+ '𑢸' => '𑣘',
+ '𑢹' => '𑣙',
+ '𑢺' => '𑣚',
+ '𑢻' => '𑣛',
+ '𑢼' => '𑣜',
+ '𑢽' => '𑣝',
+ '𑢾' => '𑣞',
+ '𑢿' => '𑣟',
+);
+
+$result =& $data;
+unset($data);
+
+return $result;
--- /dev/null
+<?php
+
+static $data = array (
+ 'a' => 'A',
+ 'b' => 'B',
+ 'c' => 'C',
+ 'd' => 'D',
+ 'e' => 'E',
+ 'f' => 'F',
+ 'g' => 'G',
+ 'h' => 'H',
+ 'i' => 'I',
+ 'j' => 'J',
+ 'k' => 'K',
+ 'l' => 'L',
+ 'm' => 'M',
+ 'n' => 'N',
+ 'o' => 'O',
+ 'p' => 'P',
+ 'q' => 'Q',
+ 'r' => 'R',
+ 's' => 'S',
+ 't' => 'T',
+ 'u' => 'U',
+ 'v' => 'V',
+ 'w' => 'W',
+ 'x' => 'X',
+ 'y' => 'Y',
+ 'z' => 'Z',
+ 'µ' => 'Μ',
+ 'à' => 'À',
+ 'á' => 'Á',
+ 'â' => 'Â',
+ 'ã' => 'Ã',
+ 'ä' => 'Ä',
+ 'å' => 'Å',
+ 'æ' => 'Æ',
+ 'ç' => 'Ç',
+ 'è' => 'È',
+ 'é' => 'É',
+ 'ê' => 'Ê',
+ 'ë' => 'Ë',
+ 'ì' => 'Ì',
+ 'í' => 'Í',
+ 'î' => 'Î',
+ 'ï' => 'Ï',
+ 'ð' => 'Ð',
+ 'ñ' => 'Ñ',
+ 'ò' => 'Ò',
+ 'ó' => 'Ó',
+ 'ô' => 'Ô',
+ 'õ' => 'Õ',
+ 'ö' => 'Ö',
+ 'ø' => 'Ø',
+ 'ù' => 'Ù',
+ 'ú' => 'Ú',
+ 'û' => 'Û',
+ 'ü' => 'Ü',
+ 'ý' => 'Ý',
+ 'þ' => 'Þ',
+ 'ÿ' => 'Ÿ',
+ 'ā' => 'Ā',
+ 'ă' => 'Ă',
+ 'ą' => 'Ą',
+ 'ć' => 'Ć',
+ 'ĉ' => 'Ĉ',
+ 'ċ' => 'Ċ',
+ 'č' => 'Č',
+ 'ď' => 'Ď',
+ 'đ' => 'Đ',
+ 'ē' => 'Ē',
+ 'ĕ' => 'Ĕ',
+ 'ė' => 'Ė',
+ 'ę' => 'Ę',
+ 'ě' => 'Ě',
+ 'ĝ' => 'Ĝ',
+ 'ğ' => 'Ğ',
+ 'ġ' => 'Ġ',
+ 'ģ' => 'Ģ',
+ 'ĥ' => 'Ĥ',
+ 'ħ' => 'Ħ',
+ 'ĩ' => 'Ĩ',
+ 'ī' => 'Ī',
+ 'ĭ' => 'Ĭ',
+ 'į' => 'Į',
+ 'ı' => 'I',
+ 'ij' => 'IJ',
+ 'ĵ' => 'Ĵ',
+ 'ķ' => 'Ķ',
+ 'ĺ' => 'Ĺ',
+ 'ļ' => 'Ļ',
+ 'ľ' => 'Ľ',
+ 'ŀ' => 'Ŀ',
+ 'ł' => 'Ł',
+ 'ń' => 'Ń',
+ 'ņ' => 'Ņ',
+ 'ň' => 'Ň',
+ 'ŋ' => 'Ŋ',
+ 'ō' => 'Ō',
+ 'ŏ' => 'Ŏ',
+ 'ő' => 'Ő',
+ 'œ' => 'Œ',
+ 'ŕ' => 'Ŕ',
+ 'ŗ' => 'Ŗ',
+ 'ř' => 'Ř',
+ 'ś' => 'Ś',
+ 'ŝ' => 'Ŝ',
+ 'ş' => 'Ş',
+ 'š' => 'Š',
+ 'ţ' => 'Ţ',
+ 'ť' => 'Ť',
+ 'ŧ' => 'Ŧ',
+ 'ũ' => 'Ũ',
+ 'ū' => 'Ū',
+ 'ŭ' => 'Ŭ',
+ 'ů' => 'Ů',
+ 'ű' => 'Ű',
+ 'ų' => 'Ų',
+ 'ŵ' => 'Ŵ',
+ 'ŷ' => 'Ŷ',
+ 'ź' => 'Ź',
+ 'ż' => 'Ż',
+ 'ž' => 'Ž',
+ 'ſ' => 'S',
+ 'ƀ' => 'Ƀ',
+ 'ƃ' => 'Ƃ',
+ 'ƅ' => 'Ƅ',
+ 'ƈ' => 'Ƈ',
+ 'ƌ' => 'Ƌ',
+ 'ƒ' => 'Ƒ',
+ 'ƕ' => 'Ƕ',
+ 'ƙ' => 'Ƙ',
+ 'ƚ' => 'Ƚ',
+ 'ƞ' => 'Ƞ',
+ 'ơ' => 'Ơ',
+ 'ƣ' => 'Ƣ',
+ 'ƥ' => 'Ƥ',
+ 'ƨ' => 'Ƨ',
+ 'ƭ' => 'Ƭ',
+ 'ư' => 'Ư',
+ 'ƴ' => 'Ƴ',
+ 'ƶ' => 'Ƶ',
+ 'ƹ' => 'Ƹ',
+ 'ƽ' => 'Ƽ',
+ 'ƿ' => 'Ƿ',
+ 'Dž' => 'DŽ',
+ 'dž' => 'DŽ',
+ 'Lj' => 'LJ',
+ 'lj' => 'LJ',
+ 'Nj' => 'NJ',
+ 'nj' => 'NJ',
+ 'ǎ' => 'Ǎ',
+ 'ǐ' => 'Ǐ',
+ 'ǒ' => 'Ǒ',
+ 'ǔ' => 'Ǔ',
+ 'ǖ' => 'Ǖ',
+ 'ǘ' => 'Ǘ',
+ 'ǚ' => 'Ǚ',
+ 'ǜ' => 'Ǜ',
+ 'ǝ' => 'Ǝ',
+ 'ǟ' => 'Ǟ',
+ 'ǡ' => 'Ǡ',
+ 'ǣ' => 'Ǣ',
+ 'ǥ' => 'Ǥ',
+ 'ǧ' => 'Ǧ',
+ 'ǩ' => 'Ǩ',
+ 'ǫ' => 'Ǫ',
+ 'ǭ' => 'Ǭ',
+ 'ǯ' => 'Ǯ',
+ 'Dz' => 'DZ',
+ 'dz' => 'DZ',
+ 'ǵ' => 'Ǵ',
+ 'ǹ' => 'Ǹ',
+ 'ǻ' => 'Ǻ',
+ 'ǽ' => 'Ǽ',
+ 'ǿ' => 'Ǿ',
+ 'ȁ' => 'Ȁ',
+ 'ȃ' => 'Ȃ',
+ 'ȅ' => 'Ȅ',
+ 'ȇ' => 'Ȇ',
+ 'ȉ' => 'Ȉ',
+ 'ȋ' => 'Ȋ',
+ 'ȍ' => 'Ȍ',
+ 'ȏ' => 'Ȏ',
+ 'ȑ' => 'Ȑ',
+ 'ȓ' => 'Ȓ',
+ 'ȕ' => 'Ȕ',
+ 'ȗ' => 'Ȗ',
+ 'ș' => 'Ș',
+ 'ț' => 'Ț',
+ 'ȝ' => 'Ȝ',
+ 'ȟ' => 'Ȟ',
+ 'ȣ' => 'Ȣ',
+ 'ȥ' => 'Ȥ',
+ 'ȧ' => 'Ȧ',
+ 'ȩ' => 'Ȩ',
+ 'ȫ' => 'Ȫ',
+ 'ȭ' => 'Ȭ',
+ 'ȯ' => 'Ȯ',
+ 'ȱ' => 'Ȱ',
+ 'ȳ' => 'Ȳ',
+ 'ȼ' => 'Ȼ',
+ 'ȿ' => 'Ȿ',
+ 'ɀ' => 'Ɀ',
+ 'ɂ' => 'Ɂ',
+ 'ɇ' => 'Ɇ',
+ 'ɉ' => 'Ɉ',
+ 'ɋ' => 'Ɋ',
+ 'ɍ' => 'Ɍ',
+ 'ɏ' => 'Ɏ',
+ 'ɐ' => 'Ɐ',
+ 'ɑ' => 'Ɑ',
+ 'ɒ' => 'Ɒ',
+ 'ɓ' => 'Ɓ',
+ 'ɔ' => 'Ɔ',
+ 'ɖ' => 'Ɖ',
+ 'ɗ' => 'Ɗ',
+ 'ə' => 'Ə',
+ 'ɛ' => 'Ɛ',
+ 'ɜ' => 'Ɜ',
+ 'ɠ' => 'Ɠ',
+ 'ɡ' => 'Ɡ',
+ 'ɣ' => 'Ɣ',
+ 'ɥ' => 'Ɥ',
+ 'ɦ' => 'Ɦ',
+ 'ɨ' => 'Ɨ',
+ 'ɩ' => 'Ɩ',
+ 'ɫ' => 'Ɫ',
+ 'ɬ' => 'Ɬ',
+ 'ɯ' => 'Ɯ',
+ 'ɱ' => 'Ɱ',
+ 'ɲ' => 'Ɲ',
+ 'ɵ' => 'Ɵ',
+ 'ɽ' => 'Ɽ',
+ 'ʀ' => 'Ʀ',
+ 'ʃ' => 'Ʃ',
+ 'ʇ' => 'Ʇ',
+ 'ʈ' => 'Ʈ',
+ 'ʉ' => 'Ʉ',
+ 'ʊ' => 'Ʊ',
+ 'ʋ' => 'Ʋ',
+ 'ʌ' => 'Ʌ',
+ 'ʒ' => 'Ʒ',
+ 'ʞ' => 'Ʞ',
+ 'ͅ' => 'Ι',
+ 'ͱ' => 'Ͱ',
+ 'ͳ' => 'Ͳ',
+ 'ͷ' => 'Ͷ',
+ 'ͻ' => 'Ͻ',
+ 'ͼ' => 'Ͼ',
+ 'ͽ' => 'Ͽ',
+ 'ά' => 'Ά',
+ 'έ' => 'Έ',
+ 'ή' => 'Ή',
+ 'ί' => 'Ί',
+ 'α' => 'Α',
+ 'β' => 'Β',
+ 'γ' => 'Γ',
+ 'δ' => 'Δ',
+ 'ε' => 'Ε',
+ 'ζ' => 'Ζ',
+ 'η' => 'Η',
+ 'θ' => 'Θ',
+ 'ι' => 'Ι',
+ 'κ' => 'Κ',
+ 'λ' => 'Λ',
+ 'μ' => 'Μ',
+ 'ν' => 'Ν',
+ 'ξ' => 'Ξ',
+ 'ο' => 'Ο',
+ 'π' => 'Π',
+ 'ρ' => 'Ρ',
+ 'ς' => 'Σ',
+ 'σ' => 'Σ',
+ 'τ' => 'Τ',
+ 'υ' => 'Υ',
+ 'φ' => 'Φ',
+ 'χ' => 'Χ',
+ 'ψ' => 'Ψ',
+ 'ω' => 'Ω',
+ 'ϊ' => 'Ϊ',
+ 'ϋ' => 'Ϋ',
+ 'ό' => 'Ό',
+ 'ύ' => 'Ύ',
+ 'ώ' => 'Ώ',
+ 'ϐ' => 'Β',
+ 'ϑ' => 'Θ',
+ 'ϕ' => 'Φ',
+ 'ϖ' => 'Π',
+ 'ϗ' => 'Ϗ',
+ 'ϙ' => 'Ϙ',
+ 'ϛ' => 'Ϛ',
+ 'ϝ' => 'Ϝ',
+ 'ϟ' => 'Ϟ',
+ 'ϡ' => 'Ϡ',
+ 'ϣ' => 'Ϣ',
+ 'ϥ' => 'Ϥ',
+ 'ϧ' => 'Ϧ',
+ 'ϩ' => 'Ϩ',
+ 'ϫ' => 'Ϫ',
+ 'ϭ' => 'Ϭ',
+ 'ϯ' => 'Ϯ',
+ 'ϰ' => 'Κ',
+ 'ϱ' => 'Ρ',
+ 'ϲ' => 'Ϲ',
+ 'ϳ' => 'Ϳ',
+ 'ϵ' => 'Ε',
+ 'ϸ' => 'Ϸ',
+ 'ϻ' => 'Ϻ',
+ 'а' => 'А',
+ 'б' => 'Б',
+ 'в' => 'В',
+ 'г' => 'Г',
+ 'д' => 'Д',
+ 'е' => 'Е',
+ 'ж' => 'Ж',
+ 'з' => 'З',
+ 'и' => 'И',
+ 'й' => 'Й',
+ 'к' => 'К',
+ 'л' => 'Л',
+ 'м' => 'М',
+ 'н' => 'Н',
+ 'о' => 'О',
+ 'п' => 'П',
+ 'р' => 'Р',
+ 'с' => 'С',
+ 'т' => 'Т',
+ 'у' => 'У',
+ 'ф' => 'Ф',
+ 'х' => 'Х',
+ 'ц' => 'Ц',
+ 'ч' => 'Ч',
+ 'ш' => 'Ш',
+ 'щ' => 'Щ',
+ 'ъ' => 'Ъ',
+ 'ы' => 'Ы',
+ 'ь' => 'Ь',
+ 'э' => 'Э',
+ 'ю' => 'Ю',
+ 'я' => 'Я',
+ 'ѐ' => 'Ѐ',
+ 'ё' => 'Ё',
+ 'ђ' => 'Ђ',
+ 'ѓ' => 'Ѓ',
+ 'є' => 'Є',
+ 'ѕ' => 'Ѕ',
+ 'і' => 'І',
+ 'ї' => 'Ї',
+ 'ј' => 'Ј',
+ 'љ' => 'Љ',
+ 'њ' => 'Њ',
+ 'ћ' => 'Ћ',
+ 'ќ' => 'Ќ',
+ 'ѝ' => 'Ѝ',
+ 'ў' => 'Ў',
+ 'џ' => 'Џ',
+ 'ѡ' => 'Ѡ',
+ 'ѣ' => 'Ѣ',
+ 'ѥ' => 'Ѥ',
+ 'ѧ' => 'Ѧ',
+ 'ѩ' => 'Ѩ',
+ 'ѫ' => 'Ѫ',
+ 'ѭ' => 'Ѭ',
+ 'ѯ' => 'Ѯ',
+ 'ѱ' => 'Ѱ',
+ 'ѳ' => 'Ѳ',
+ 'ѵ' => 'Ѵ',
+ 'ѷ' => 'Ѷ',
+ 'ѹ' => 'Ѹ',
+ 'ѻ' => 'Ѻ',
+ 'ѽ' => 'Ѽ',
+ 'ѿ' => 'Ѿ',
+ 'ҁ' => 'Ҁ',
+ 'ҋ' => 'Ҋ',
+ 'ҍ' => 'Ҍ',
+ 'ҏ' => 'Ҏ',
+ 'ґ' => 'Ґ',
+ 'ғ' => 'Ғ',
+ 'ҕ' => 'Ҕ',
+ 'җ' => 'Җ',
+ 'ҙ' => 'Ҙ',
+ 'қ' => 'Қ',
+ 'ҝ' => 'Ҝ',
+ 'ҟ' => 'Ҟ',
+ 'ҡ' => 'Ҡ',
+ 'ң' => 'Ң',
+ 'ҥ' => 'Ҥ',
+ 'ҧ' => 'Ҧ',
+ 'ҩ' => 'Ҩ',
+ 'ҫ' => 'Ҫ',
+ 'ҭ' => 'Ҭ',
+ 'ү' => 'Ү',
+ 'ұ' => 'Ұ',
+ 'ҳ' => 'Ҳ',
+ 'ҵ' => 'Ҵ',
+ 'ҷ' => 'Ҷ',
+ 'ҹ' => 'Ҹ',
+ 'һ' => 'Һ',
+ 'ҽ' => 'Ҽ',
+ 'ҿ' => 'Ҿ',
+ 'ӂ' => 'Ӂ',
+ 'ӄ' => 'Ӄ',
+ 'ӆ' => 'Ӆ',
+ 'ӈ' => 'Ӈ',
+ 'ӊ' => 'Ӊ',
+ 'ӌ' => 'Ӌ',
+ 'ӎ' => 'Ӎ',
+ 'ӏ' => 'Ӏ',
+ 'ӑ' => 'Ӑ',
+ 'ӓ' => 'Ӓ',
+ 'ӕ' => 'Ӕ',
+ 'ӗ' => 'Ӗ',
+ 'ә' => 'Ә',
+ 'ӛ' => 'Ӛ',
+ 'ӝ' => 'Ӝ',
+ 'ӟ' => 'Ӟ',
+ 'ӡ' => 'Ӡ',
+ 'ӣ' => 'Ӣ',
+ 'ӥ' => 'Ӥ',
+ 'ӧ' => 'Ӧ',
+ 'ө' => 'Ө',
+ 'ӫ' => 'Ӫ',
+ 'ӭ' => 'Ӭ',
+ 'ӯ' => 'Ӯ',
+ 'ӱ' => 'Ӱ',
+ 'ӳ' => 'Ӳ',
+ 'ӵ' => 'Ӵ',
+ 'ӷ' => 'Ӷ',
+ 'ӹ' => 'Ӹ',
+ 'ӻ' => 'Ӻ',
+ 'ӽ' => 'Ӽ',
+ 'ӿ' => 'Ӿ',
+ 'ԁ' => 'Ԁ',
+ 'ԃ' => 'Ԃ',
+ 'ԅ' => 'Ԅ',
+ 'ԇ' => 'Ԇ',
+ 'ԉ' => 'Ԉ',
+ 'ԋ' => 'Ԋ',
+ 'ԍ' => 'Ԍ',
+ 'ԏ' => 'Ԏ',
+ 'ԑ' => 'Ԑ',
+ 'ԓ' => 'Ԓ',
+ 'ԕ' => 'Ԕ',
+ 'ԗ' => 'Ԗ',
+ 'ԙ' => 'Ԙ',
+ 'ԛ' => 'Ԛ',
+ 'ԝ' => 'Ԝ',
+ 'ԟ' => 'Ԟ',
+ 'ԡ' => 'Ԡ',
+ 'ԣ' => 'Ԣ',
+ 'ԥ' => 'Ԥ',
+ 'ԧ' => 'Ԧ',
+ 'ԩ' => 'Ԩ',
+ 'ԫ' => 'Ԫ',
+ 'ԭ' => 'Ԭ',
+ 'ԯ' => 'Ԯ',
+ 'ա' => 'Ա',
+ 'բ' => 'Բ',
+ 'գ' => 'Գ',
+ 'դ' => 'Դ',
+ 'ե' => 'Ե',
+ 'զ' => 'Զ',
+ 'է' => 'Է',
+ 'ը' => 'Ը',
+ 'թ' => 'Թ',
+ 'ժ' => 'Ժ',
+ 'ի' => 'Ի',
+ 'լ' => 'Լ',
+ 'խ' => 'Խ',
+ 'ծ' => 'Ծ',
+ 'կ' => 'Կ',
+ 'հ' => 'Հ',
+ 'ձ' => 'Ձ',
+ 'ղ' => 'Ղ',
+ 'ճ' => 'Ճ',
+ 'մ' => 'Մ',
+ 'յ' => 'Յ',
+ 'ն' => 'Ն',
+ 'շ' => 'Շ',
+ 'ո' => 'Ո',
+ 'չ' => 'Չ',
+ 'պ' => 'Պ',
+ 'ջ' => 'Ջ',
+ 'ռ' => 'Ռ',
+ 'ս' => 'Ս',
+ 'վ' => 'Վ',
+ 'տ' => 'Տ',
+ 'ր' => 'Ր',
+ 'ց' => 'Ց',
+ 'ւ' => 'Ւ',
+ 'փ' => 'Փ',
+ 'ք' => 'Ք',
+ 'օ' => 'Օ',
+ 'ֆ' => 'Ֆ',
+ 'ᵹ' => 'Ᵹ',
+ 'ᵽ' => 'Ᵽ',
+ 'ḁ' => 'Ḁ',
+ 'ḃ' => 'Ḃ',
+ 'ḅ' => 'Ḅ',
+ 'ḇ' => 'Ḇ',
+ 'ḉ' => 'Ḉ',
+ 'ḋ' => 'Ḋ',
+ 'ḍ' => 'Ḍ',
+ 'ḏ' => 'Ḏ',
+ 'ḑ' => 'Ḑ',
+ 'ḓ' => 'Ḓ',
+ 'ḕ' => 'Ḕ',
+ 'ḗ' => 'Ḗ',
+ 'ḙ' => 'Ḙ',
+ 'ḛ' => 'Ḛ',
+ 'ḝ' => 'Ḝ',
+ 'ḟ' => 'Ḟ',
+ 'ḡ' => 'Ḡ',
+ 'ḣ' => 'Ḣ',
+ 'ḥ' => 'Ḥ',
+ 'ḧ' => 'Ḧ',
+ 'ḩ' => 'Ḩ',
+ 'ḫ' => 'Ḫ',
+ 'ḭ' => 'Ḭ',
+ 'ḯ' => 'Ḯ',
+ 'ḱ' => 'Ḱ',
+ 'ḳ' => 'Ḳ',
+ 'ḵ' => 'Ḵ',
+ 'ḷ' => 'Ḷ',
+ 'ḹ' => 'Ḹ',
+ 'ḻ' => 'Ḻ',
+ 'ḽ' => 'Ḽ',
+ 'ḿ' => 'Ḿ',
+ 'ṁ' => 'Ṁ',
+ 'ṃ' => 'Ṃ',
+ 'ṅ' => 'Ṅ',
+ 'ṇ' => 'Ṇ',
+ 'ṉ' => 'Ṉ',
+ 'ṋ' => 'Ṋ',
+ 'ṍ' => 'Ṍ',
+ 'ṏ' => 'Ṏ',
+ 'ṑ' => 'Ṑ',
+ 'ṓ' => 'Ṓ',
+ 'ṕ' => 'Ṕ',
+ 'ṗ' => 'Ṗ',
+ 'ṙ' => 'Ṙ',
+ 'ṛ' => 'Ṛ',
+ 'ṝ' => 'Ṝ',
+ 'ṟ' => 'Ṟ',
+ 'ṡ' => 'Ṡ',
+ 'ṣ' => 'Ṣ',
+ 'ṥ' => 'Ṥ',
+ 'ṧ' => 'Ṧ',
+ 'ṩ' => 'Ṩ',
+ 'ṫ' => 'Ṫ',
+ 'ṭ' => 'Ṭ',
+ 'ṯ' => 'Ṯ',
+ 'ṱ' => 'Ṱ',
+ 'ṳ' => 'Ṳ',
+ 'ṵ' => 'Ṵ',
+ 'ṷ' => 'Ṷ',
+ 'ṹ' => 'Ṹ',
+ 'ṻ' => 'Ṻ',
+ 'ṽ' => 'Ṽ',
+ 'ṿ' => 'Ṿ',
+ 'ẁ' => 'Ẁ',
+ 'ẃ' => 'Ẃ',
+ 'ẅ' => 'Ẅ',
+ 'ẇ' => 'Ẇ',
+ 'ẉ' => 'Ẉ',
+ 'ẋ' => 'Ẋ',
+ 'ẍ' => 'Ẍ',
+ 'ẏ' => 'Ẏ',
+ 'ẑ' => 'Ẑ',
+ 'ẓ' => 'Ẓ',
+ 'ẕ' => 'Ẕ',
+ 'ẛ' => 'Ṡ',
+ 'ạ' => 'Ạ',
+ 'ả' => 'Ả',
+ 'ấ' => 'Ấ',
+ 'ầ' => 'Ầ',
+ 'ẩ' => 'Ẩ',
+ 'ẫ' => 'Ẫ',
+ 'ậ' => 'Ậ',
+ 'ắ' => 'Ắ',
+ 'ằ' => 'Ằ',
+ 'ẳ' => 'Ẳ',
+ 'ẵ' => 'Ẵ',
+ 'ặ' => 'Ặ',
+ 'ẹ' => 'Ẹ',
+ 'ẻ' => 'Ẻ',
+ 'ẽ' => 'Ẽ',
+ 'ế' => 'Ế',
+ 'ề' => 'Ề',
+ 'ể' => 'Ể',
+ 'ễ' => 'Ễ',
+ 'ệ' => 'Ệ',
+ 'ỉ' => 'Ỉ',
+ 'ị' => 'Ị',
+ 'ọ' => 'Ọ',
+ 'ỏ' => 'Ỏ',
+ 'ố' => 'Ố',
+ 'ồ' => 'Ồ',
+ 'ổ' => 'Ổ',
+ 'ỗ' => 'Ỗ',
+ 'ộ' => 'Ộ',
+ 'ớ' => 'Ớ',
+ 'ờ' => 'Ờ',
+ 'ở' => 'Ở',
+ 'ỡ' => 'Ỡ',
+ 'ợ' => 'Ợ',
+ 'ụ' => 'Ụ',
+ 'ủ' => 'Ủ',
+ 'ứ' => 'Ứ',
+ 'ừ' => 'Ừ',
+ 'ử' => 'Ử',
+ 'ữ' => 'Ữ',
+ 'ự' => 'Ự',
+ 'ỳ' => 'Ỳ',
+ 'ỵ' => 'Ỵ',
+ 'ỷ' => 'Ỷ',
+ 'ỹ' => 'Ỹ',
+ 'ỻ' => 'Ỻ',
+ 'ỽ' => 'Ỽ',
+ 'ỿ' => 'Ỿ',
+ 'ἀ' => 'Ἀ',
+ 'ἁ' => 'Ἁ',
+ 'ἂ' => 'Ἂ',
+ 'ἃ' => 'Ἃ',
+ 'ἄ' => 'Ἄ',
+ 'ἅ' => 'Ἅ',
+ 'ἆ' => 'Ἆ',
+ 'ἇ' => 'Ἇ',
+ 'ἐ' => 'Ἐ',
+ 'ἑ' => 'Ἑ',
+ 'ἒ' => 'Ἒ',
+ 'ἓ' => 'Ἓ',
+ 'ἔ' => 'Ἔ',
+ 'ἕ' => 'Ἕ',
+ 'ἠ' => 'Ἠ',
+ 'ἡ' => 'Ἡ',
+ 'ἢ' => 'Ἢ',
+ 'ἣ' => 'Ἣ',
+ 'ἤ' => 'Ἤ',
+ 'ἥ' => 'Ἥ',
+ 'ἦ' => 'Ἦ',
+ 'ἧ' => 'Ἧ',
+ 'ἰ' => 'Ἰ',
+ 'ἱ' => 'Ἱ',
+ 'ἲ' => 'Ἲ',
+ 'ἳ' => 'Ἳ',
+ 'ἴ' => 'Ἴ',
+ 'ἵ' => 'Ἵ',
+ 'ἶ' => 'Ἶ',
+ 'ἷ' => 'Ἷ',
+ 'ὀ' => 'Ὀ',
+ 'ὁ' => 'Ὁ',
+ 'ὂ' => 'Ὂ',
+ 'ὃ' => 'Ὃ',
+ 'ὄ' => 'Ὄ',
+ 'ὅ' => 'Ὅ',
+ 'ὑ' => 'Ὑ',
+ 'ὓ' => 'Ὓ',
+ 'ὕ' => 'Ὕ',
+ 'ὗ' => 'Ὗ',
+ 'ὠ' => 'Ὠ',
+ 'ὡ' => 'Ὡ',
+ 'ὢ' => 'Ὢ',
+ 'ὣ' => 'Ὣ',
+ 'ὤ' => 'Ὤ',
+ 'ὥ' => 'Ὥ',
+ 'ὦ' => 'Ὦ',
+ 'ὧ' => 'Ὧ',
+ 'ὰ' => 'Ὰ',
+ 'ά' => 'Ά',
+ 'ὲ' => 'Ὲ',
+ 'έ' => 'Έ',
+ 'ὴ' => 'Ὴ',
+ 'ή' => 'Ή',
+ 'ὶ' => 'Ὶ',
+ 'ί' => 'Ί',
+ 'ὸ' => 'Ὸ',
+ 'ό' => 'Ό',
+ 'ὺ' => 'Ὺ',
+ 'ύ' => 'Ύ',
+ 'ὼ' => 'Ὼ',
+ 'ώ' => 'Ώ',
+ 'ᾀ' => 'ᾈ',
+ 'ᾁ' => 'ᾉ',
+ 'ᾂ' => 'ᾊ',
+ 'ᾃ' => 'ᾋ',
+ 'ᾄ' => 'ᾌ',
+ 'ᾅ' => 'ᾍ',
+ 'ᾆ' => 'ᾎ',
+ 'ᾇ' => 'ᾏ',
+ 'ᾐ' => 'ᾘ',
+ 'ᾑ' => 'ᾙ',
+ 'ᾒ' => 'ᾚ',
+ 'ᾓ' => 'ᾛ',
+ 'ᾔ' => 'ᾜ',
+ 'ᾕ' => 'ᾝ',
+ 'ᾖ' => 'ᾞ',
+ 'ᾗ' => 'ᾟ',
+ 'ᾠ' => 'ᾨ',
+ 'ᾡ' => 'ᾩ',
+ 'ᾢ' => 'ᾪ',
+ 'ᾣ' => 'ᾫ',
+ 'ᾤ' => 'ᾬ',
+ 'ᾥ' => 'ᾭ',
+ 'ᾦ' => 'ᾮ',
+ 'ᾧ' => 'ᾯ',
+ 'ᾰ' => 'Ᾰ',
+ 'ᾱ' => 'Ᾱ',
+ 'ᾳ' => 'ᾼ',
+ 'ι' => 'Ι',
+ 'ῃ' => 'ῌ',
+ 'ῐ' => 'Ῐ',
+ 'ῑ' => 'Ῑ',
+ 'ῠ' => 'Ῠ',
+ 'ῡ' => 'Ῡ',
+ 'ῥ' => 'Ῥ',
+ 'ῳ' => 'ῼ',
+ 'ⅎ' => 'Ⅎ',
+ 'ⅰ' => 'Ⅰ',
+ 'ⅱ' => 'Ⅱ',
+ 'ⅲ' => 'Ⅲ',
+ 'ⅳ' => 'Ⅳ',
+ 'ⅴ' => 'Ⅴ',
+ 'ⅵ' => 'Ⅵ',
+ 'ⅶ' => 'Ⅶ',
+ 'ⅷ' => 'Ⅷ',
+ 'ⅸ' => 'Ⅸ',
+ 'ⅹ' => 'Ⅹ',
+ 'ⅺ' => 'Ⅺ',
+ 'ⅻ' => 'Ⅻ',
+ 'ⅼ' => 'Ⅼ',
+ 'ⅽ' => 'Ⅽ',
+ 'ⅾ' => 'Ⅾ',
+ 'ⅿ' => 'Ⅿ',
+ 'ↄ' => 'Ↄ',
+ 'ⓐ' => 'Ⓐ',
+ 'ⓑ' => 'Ⓑ',
+ 'ⓒ' => 'Ⓒ',
+ 'ⓓ' => 'Ⓓ',
+ 'ⓔ' => 'Ⓔ',
+ 'ⓕ' => 'Ⓕ',
+ 'ⓖ' => 'Ⓖ',
+ 'ⓗ' => 'Ⓗ',
+ 'ⓘ' => 'Ⓘ',
+ 'ⓙ' => 'Ⓙ',
+ 'ⓚ' => 'Ⓚ',
+ 'ⓛ' => 'Ⓛ',
+ 'ⓜ' => 'Ⓜ',
+ 'ⓝ' => 'Ⓝ',
+ 'ⓞ' => 'Ⓞ',
+ 'ⓟ' => 'Ⓟ',
+ 'ⓠ' => 'Ⓠ',
+ 'ⓡ' => 'Ⓡ',
+ 'ⓢ' => 'Ⓢ',
+ 'ⓣ' => 'Ⓣ',
+ 'ⓤ' => 'Ⓤ',
+ 'ⓥ' => 'Ⓥ',
+ 'ⓦ' => 'Ⓦ',
+ 'ⓧ' => 'Ⓧ',
+ 'ⓨ' => 'Ⓨ',
+ 'ⓩ' => 'Ⓩ',
+ 'ⰰ' => 'Ⰰ',
+ 'ⰱ' => 'Ⰱ',
+ 'ⰲ' => 'Ⰲ',
+ 'ⰳ' => 'Ⰳ',
+ 'ⰴ' => 'Ⰴ',
+ 'ⰵ' => 'Ⰵ',
+ 'ⰶ' => 'Ⰶ',
+ 'ⰷ' => 'Ⰷ',
+ 'ⰸ' => 'Ⰸ',
+ 'ⰹ' => 'Ⰹ',
+ 'ⰺ' => 'Ⰺ',
+ 'ⰻ' => 'Ⰻ',
+ 'ⰼ' => 'Ⰼ',
+ 'ⰽ' => 'Ⰽ',
+ 'ⰾ' => 'Ⰾ',
+ 'ⰿ' => 'Ⰿ',
+ 'ⱀ' => 'Ⱀ',
+ 'ⱁ' => 'Ⱁ',
+ 'ⱂ' => 'Ⱂ',
+ 'ⱃ' => 'Ⱃ',
+ 'ⱄ' => 'Ⱄ',
+ 'ⱅ' => 'Ⱅ',
+ 'ⱆ' => 'Ⱆ',
+ 'ⱇ' => 'Ⱇ',
+ 'ⱈ' => 'Ⱈ',
+ 'ⱉ' => 'Ⱉ',
+ 'ⱊ' => 'Ⱊ',
+ 'ⱋ' => 'Ⱋ',
+ 'ⱌ' => 'Ⱌ',
+ 'ⱍ' => 'Ⱍ',
+ 'ⱎ' => 'Ⱎ',
+ 'ⱏ' => 'Ⱏ',
+ 'ⱐ' => 'Ⱐ',
+ 'ⱑ' => 'Ⱑ',
+ 'ⱒ' => 'Ⱒ',
+ 'ⱓ' => 'Ⱓ',
+ 'ⱔ' => 'Ⱔ',
+ 'ⱕ' => 'Ⱕ',
+ 'ⱖ' => 'Ⱖ',
+ 'ⱗ' => 'Ⱗ',
+ 'ⱘ' => 'Ⱘ',
+ 'ⱙ' => 'Ⱙ',
+ 'ⱚ' => 'Ⱚ',
+ 'ⱛ' => 'Ⱛ',
+ 'ⱜ' => 'Ⱜ',
+ 'ⱝ' => 'Ⱝ',
+ 'ⱞ' => 'Ⱞ',
+ 'ⱡ' => 'Ⱡ',
+ 'ⱥ' => 'Ⱥ',
+ 'ⱦ' => 'Ⱦ',
+ 'ⱨ' => 'Ⱨ',
+ 'ⱪ' => 'Ⱪ',
+ 'ⱬ' => 'Ⱬ',
+ 'ⱳ' => 'Ⱳ',
+ 'ⱶ' => 'Ⱶ',
+ 'ⲁ' => 'Ⲁ',
+ 'ⲃ' => 'Ⲃ',
+ 'ⲅ' => 'Ⲅ',
+ 'ⲇ' => 'Ⲇ',
+ 'ⲉ' => 'Ⲉ',
+ 'ⲋ' => 'Ⲋ',
+ 'ⲍ' => 'Ⲍ',
+ 'ⲏ' => 'Ⲏ',
+ 'ⲑ' => 'Ⲑ',
+ 'ⲓ' => 'Ⲓ',
+ 'ⲕ' => 'Ⲕ',
+ 'ⲗ' => 'Ⲗ',
+ 'ⲙ' => 'Ⲙ',
+ 'ⲛ' => 'Ⲛ',
+ 'ⲝ' => 'Ⲝ',
+ 'ⲟ' => 'Ⲟ',
+ 'ⲡ' => 'Ⲡ',
+ 'ⲣ' => 'Ⲣ',
+ 'ⲥ' => 'Ⲥ',
+ 'ⲧ' => 'Ⲧ',
+ 'ⲩ' => 'Ⲩ',
+ 'ⲫ' => 'Ⲫ',
+ 'ⲭ' => 'Ⲭ',
+ 'ⲯ' => 'Ⲯ',
+ 'ⲱ' => 'Ⲱ',
+ 'ⲳ' => 'Ⲳ',
+ 'ⲵ' => 'Ⲵ',
+ 'ⲷ' => 'Ⲷ',
+ 'ⲹ' => 'Ⲹ',
+ 'ⲻ' => 'Ⲻ',
+ 'ⲽ' => 'Ⲽ',
+ 'ⲿ' => 'Ⲿ',
+ 'ⳁ' => 'Ⳁ',
+ 'ⳃ' => 'Ⳃ',
+ 'ⳅ' => 'Ⳅ',
+ 'ⳇ' => 'Ⳇ',
+ 'ⳉ' => 'Ⳉ',
+ 'ⳋ' => 'Ⳋ',
+ 'ⳍ' => 'Ⳍ',
+ 'ⳏ' => 'Ⳏ',
+ 'ⳑ' => 'Ⳑ',
+ 'ⳓ' => 'Ⳓ',
+ 'ⳕ' => 'Ⳕ',
+ 'ⳗ' => 'Ⳗ',
+ 'ⳙ' => 'Ⳙ',
+ 'ⳛ' => 'Ⳛ',
+ 'ⳝ' => 'Ⳝ',
+ 'ⳟ' => 'Ⳟ',
+ 'ⳡ' => 'Ⳡ',
+ 'ⳣ' => 'Ⳣ',
+ 'ⳬ' => 'Ⳬ',
+ 'ⳮ' => 'Ⳮ',
+ 'ⳳ' => 'Ⳳ',
+ 'ⴀ' => 'Ⴀ',
+ 'ⴁ' => 'Ⴁ',
+ 'ⴂ' => 'Ⴂ',
+ 'ⴃ' => 'Ⴃ',
+ 'ⴄ' => 'Ⴄ',
+ 'ⴅ' => 'Ⴅ',
+ 'ⴆ' => 'Ⴆ',
+ 'ⴇ' => 'Ⴇ',
+ 'ⴈ' => 'Ⴈ',
+ 'ⴉ' => 'Ⴉ',
+ 'ⴊ' => 'Ⴊ',
+ 'ⴋ' => 'Ⴋ',
+ 'ⴌ' => 'Ⴌ',
+ 'ⴍ' => 'Ⴍ',
+ 'ⴎ' => 'Ⴎ',
+ 'ⴏ' => 'Ⴏ',
+ 'ⴐ' => 'Ⴐ',
+ 'ⴑ' => 'Ⴑ',
+ 'ⴒ' => 'Ⴒ',
+ 'ⴓ' => 'Ⴓ',
+ 'ⴔ' => 'Ⴔ',
+ 'ⴕ' => 'Ⴕ',
+ 'ⴖ' => 'Ⴖ',
+ 'ⴗ' => 'Ⴗ',
+ 'ⴘ' => 'Ⴘ',
+ 'ⴙ' => 'Ⴙ',
+ 'ⴚ' => 'Ⴚ',
+ 'ⴛ' => 'Ⴛ',
+ 'ⴜ' => 'Ⴜ',
+ 'ⴝ' => 'Ⴝ',
+ 'ⴞ' => 'Ⴞ',
+ 'ⴟ' => 'Ⴟ',
+ 'ⴠ' => 'Ⴠ',
+ 'ⴡ' => 'Ⴡ',
+ 'ⴢ' => 'Ⴢ',
+ 'ⴣ' => 'Ⴣ',
+ 'ⴤ' => 'Ⴤ',
+ 'ⴥ' => 'Ⴥ',
+ 'ⴧ' => 'Ⴧ',
+ 'ⴭ' => 'Ⴭ',
+ 'ꙁ' => 'Ꙁ',
+ 'ꙃ' => 'Ꙃ',
+ 'ꙅ' => 'Ꙅ',
+ 'ꙇ' => 'Ꙇ',
+ 'ꙉ' => 'Ꙉ',
+ 'ꙋ' => 'Ꙋ',
+ 'ꙍ' => 'Ꙍ',
+ 'ꙏ' => 'Ꙏ',
+ 'ꙑ' => 'Ꙑ',
+ 'ꙓ' => 'Ꙓ',
+ 'ꙕ' => 'Ꙕ',
+ 'ꙗ' => 'Ꙗ',
+ 'ꙙ' => 'Ꙙ',
+ 'ꙛ' => 'Ꙛ',
+ 'ꙝ' => 'Ꙝ',
+ 'ꙟ' => 'Ꙟ',
+ 'ꙡ' => 'Ꙡ',
+ 'ꙣ' => 'Ꙣ',
+ 'ꙥ' => 'Ꙥ',
+ 'ꙧ' => 'Ꙧ',
+ 'ꙩ' => 'Ꙩ',
+ 'ꙫ' => 'Ꙫ',
+ 'ꙭ' => 'Ꙭ',
+ 'ꚁ' => 'Ꚁ',
+ 'ꚃ' => 'Ꚃ',
+ 'ꚅ' => 'Ꚅ',
+ 'ꚇ' => 'Ꚇ',
+ 'ꚉ' => 'Ꚉ',
+ 'ꚋ' => 'Ꚋ',
+ 'ꚍ' => 'Ꚍ',
+ 'ꚏ' => 'Ꚏ',
+ 'ꚑ' => 'Ꚑ',
+ 'ꚓ' => 'Ꚓ',
+ 'ꚕ' => 'Ꚕ',
+ 'ꚗ' => 'Ꚗ',
+ 'ꚙ' => 'Ꚙ',
+ 'ꚛ' => 'Ꚛ',
+ 'ꜣ' => 'Ꜣ',
+ 'ꜥ' => 'Ꜥ',
+ 'ꜧ' => 'Ꜧ',
+ 'ꜩ' => 'Ꜩ',
+ 'ꜫ' => 'Ꜫ',
+ 'ꜭ' => 'Ꜭ',
+ 'ꜯ' => 'Ꜯ',
+ 'ꜳ' => 'Ꜳ',
+ 'ꜵ' => 'Ꜵ',
+ 'ꜷ' => 'Ꜷ',
+ 'ꜹ' => 'Ꜹ',
+ 'ꜻ' => 'Ꜻ',
+ 'ꜽ' => 'Ꜽ',
+ 'ꜿ' => 'Ꜿ',
+ 'ꝁ' => 'Ꝁ',
+ 'ꝃ' => 'Ꝃ',
+ 'ꝅ' => 'Ꝅ',
+ 'ꝇ' => 'Ꝇ',
+ 'ꝉ' => 'Ꝉ',
+ 'ꝋ' => 'Ꝋ',
+ 'ꝍ' => 'Ꝍ',
+ 'ꝏ' => 'Ꝏ',
+ 'ꝑ' => 'Ꝑ',
+ 'ꝓ' => 'Ꝓ',
+ 'ꝕ' => 'Ꝕ',
+ 'ꝗ' => 'Ꝗ',
+ 'ꝙ' => 'Ꝙ',
+ 'ꝛ' => 'Ꝛ',
+ 'ꝝ' => 'Ꝝ',
+ 'ꝟ' => 'Ꝟ',
+ 'ꝡ' => 'Ꝡ',
+ 'ꝣ' => 'Ꝣ',
+ 'ꝥ' => 'Ꝥ',
+ 'ꝧ' => 'Ꝧ',
+ 'ꝩ' => 'Ꝩ',
+ 'ꝫ' => 'Ꝫ',
+ 'ꝭ' => 'Ꝭ',
+ 'ꝯ' => 'Ꝯ',
+ 'ꝺ' => 'Ꝺ',
+ 'ꝼ' => 'Ꝼ',
+ 'ꝿ' => 'Ꝿ',
+ 'ꞁ' => 'Ꞁ',
+ 'ꞃ' => 'Ꞃ',
+ 'ꞅ' => 'Ꞅ',
+ 'ꞇ' => 'Ꞇ',
+ 'ꞌ' => 'Ꞌ',
+ 'ꞑ' => 'Ꞑ',
+ 'ꞓ' => 'Ꞓ',
+ 'ꞗ' => 'Ꞗ',
+ 'ꞙ' => 'Ꞙ',
+ 'ꞛ' => 'Ꞛ',
+ 'ꞝ' => 'Ꞝ',
+ 'ꞟ' => 'Ꞟ',
+ 'ꞡ' => 'Ꞡ',
+ 'ꞣ' => 'Ꞣ',
+ 'ꞥ' => 'Ꞥ',
+ 'ꞧ' => 'Ꞧ',
+ 'ꞩ' => 'Ꞩ',
+ 'a' => 'A',
+ 'b' => 'B',
+ 'c' => 'C',
+ 'd' => 'D',
+ 'e' => 'E',
+ 'f' => 'F',
+ 'g' => 'G',
+ 'h' => 'H',
+ 'i' => 'I',
+ 'j' => 'J',
+ 'k' => 'K',
+ 'l' => 'L',
+ 'm' => 'M',
+ 'n' => 'N',
+ 'o' => 'O',
+ 'p' => 'P',
+ 'q' => 'Q',
+ 'r' => 'R',
+ 's' => 'S',
+ 't' => 'T',
+ 'u' => 'U',
+ 'v' => 'V',
+ 'w' => 'W',
+ 'x' => 'X',
+ 'y' => 'Y',
+ 'z' => 'Z',
+ '𐐨' => '𐐀',
+ '𐐩' => '𐐁',
+ '𐐪' => '𐐂',
+ '𐐫' => '𐐃',
+ '𐐬' => '𐐄',
+ '𐐭' => '𐐅',
+ '𐐮' => '𐐆',
+ '𐐯' => '𐐇',
+ '𐐰' => '𐐈',
+ '𐐱' => '𐐉',
+ '𐐲' => '𐐊',
+ '𐐳' => '𐐋',
+ '𐐴' => '𐐌',
+ '𐐵' => '𐐍',
+ '𐐶' => '𐐎',
+ '𐐷' => '𐐏',
+ '𐐸' => '𐐐',
+ '𐐹' => '𐐑',
+ '𐐺' => '𐐒',
+ '𐐻' => '𐐓',
+ '𐐼' => '𐐔',
+ '𐐽' => '𐐕',
+ '𐐾' => '𐐖',
+ '𐐿' => '𐐗',
+ '𐑀' => '𐐘',
+ '𐑁' => '𐐙',
+ '𐑂' => '𐐚',
+ '𐑃' => '𐐛',
+ '𐑄' => '𐐜',
+ '𐑅' => '𐐝',
+ '𐑆' => '𐐞',
+ '𐑇' => '𐐟',
+ '𐑈' => '𐐠',
+ '𐑉' => '𐐡',
+ '𐑊' => '𐐢',
+ '𐑋' => '𐐣',
+ '𐑌' => '𐐤',
+ '𐑍' => '𐐥',
+ '𐑎' => '𐐦',
+ '𐑏' => '𐐧',
+ '𑣀' => '𑢠',
+ '𑣁' => '𑢡',
+ '𑣂' => '𑢢',
+ '𑣃' => '𑢣',
+ '𑣄' => '𑢤',
+ '𑣅' => '𑢥',
+ '𑣆' => '𑢦',
+ '𑣇' => '𑢧',
+ '𑣈' => '𑢨',
+ '𑣉' => '𑢩',
+ '𑣊' => '𑢪',
+ '𑣋' => '𑢫',
+ '𑣌' => '𑢬',
+ '𑣍' => '𑢭',
+ '𑣎' => '𑢮',
+ '𑣏' => '𑢯',
+ '𑣐' => '𑢰',
+ '𑣑' => '𑢱',
+ '𑣒' => '𑢲',
+ '𑣓' => '𑢳',
+ '𑣔' => '𑢴',
+ '𑣕' => '𑢵',
+ '𑣖' => '𑢶',
+ '𑣗' => '𑢷',
+ '𑣘' => '𑢸',
+ '𑣙' => '𑢹',
+ '𑣚' => '𑢺',
+ '𑣛' => '𑢻',
+ '𑣜' => '𑢼',
+ '𑣝' => '𑢽',
+ '𑣞' => '𑢾',
+ '𑣟' => '𑢿',
+);
+
+$result =& $data;
+unset($data);
+
+return $result;
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Polyfill\Mbstring as p;
+
+if (!function_exists('mb_strlen')) {
+ define('MB_CASE_UPPER', 0);
+ define('MB_CASE_LOWER', 1);
+ define('MB_CASE_TITLE', 2);
+
+ function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); }
+ function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); }
+ function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); }
+ function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); }
+ function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); }
+ function mb_language($lang = null) { return p\Mbstring::mb_language($lang); }
+ function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); }
+ function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); }
+ function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); }
+ function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); }
+ function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); }
+ function mb_parse_str($s, &$result = array()) { parse_str($s, $result); }
+ function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); }
+ function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); }
+ function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); }
+ function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); }
+ function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); }
+ function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); }
+ function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); }
+ function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); }
+ function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); }
+ function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); }
+ function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); }
+ function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); }
+ function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); }
+ function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); }
+ function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); }
+ function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); }
+ function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); }
+ function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); }
+ function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); }
+ function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); }
+}
+if (!function_exists('mb_chr')) {
+ function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); }
+ function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); }
+ function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); }
+}
--- /dev/null
+{
+ "name": "symfony/polyfill-mbstring",
+ "type": "library",
+ "description": "Symfony polyfill for the Mbstring extension",
+ "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" },
+ "files": [ "bootstrap.php" ]
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6-dev"
+ }
+ }
+}
--- /dev/null
+vendor/
+composer.lock
+phpunit.xml
--- /dev/null
+CHANGELOG
+=========
+
+3.2.0
+-----
+
+ * Added support for escaping `|` in plural translations with double pipe.
+
+3.1.0
+-----
+
+ * Deprecated the backup feature of the file dumper classes.
+
+3.0.0
+-----
+
+ * removed `FileDumper::format()` method.
+ * Changed the visibility of the locale property in `Translator` from protected to private.
+
+2.8.0
+-----
+
+ * deprecated FileDumper::format(), overwrite FileDumper::formatCatalogue() instead.
+ * deprecated Translator::getMessages(), rely on TranslatorBagInterface::getCatalogue() instead.
+ * added `FileDumper::formatCatalogue` which allows format the catalogue without dumping it into file.
+ * added option `json_encoding` to JsonFileDumper
+ * added options `as_tree`, `inline` to YamlFileDumper
+ * added support for XLIFF 2.0.
+ * added support for XLIFF target and tool attributes.
+ * added message parameters to DataCollectorTranslator.
+ * [DEPRECATION] The `DiffOperation` class has been deprecated and
+ will be removed in Symfony 3.0, since its operation has nothing to do with 'diff',
+ so the class name is misleading. The `TargetOperation` class should be used for
+ this use-case instead.
+
+2.7.0
+-----
+
+ * added DataCollectorTranslator for collecting the translated messages.
+
+2.6.0
+-----
+
+ * added possibility to cache catalogues
+ * added TranslatorBagInterface
+ * added LoggingTranslator
+ * added Translator::getMessages() for retrieving the message catalogue as an array
+
+2.5.0
+-----
+
+ * added relative file path template to the file dumpers
+ * added optional backup to the file dumpers
+ * changed IcuResFileDumper to extend FileDumper
+
+2.3.0
+-----
+
+ * added classes to make operations on catalogues (like making a diff or a merge on 2 catalogues)
+ * added Translator::getFallbackLocales()
+ * deprecated Translator::setFallbackLocale() in favor of the new Translator::setFallbackLocales() method
+
+2.2.0
+-----
+
+ * QtTranslationsLoader class renamed to QtFileLoader. QtTranslationsLoader is deprecated and will be removed in 2.3.
+ * [BC BREAK] uniformized the exception thrown by the load() method when an error occurs. The load() method now
+ throws Symfony\Component\Translation\Exception\NotFoundResourceException when a resource cannot be found
+ and Symfony\Component\Translation\Exception\InvalidResourceException when a resource is invalid.
+ * changed the exception class thrown by some load() methods from \RuntimeException to \InvalidArgumentException
+ (IcuDatFileLoader, IcuResFileLoader and QtFileLoader)
+
+2.1.0
+-----
+
+ * added support for more than one fallback locale
+ * added support for extracting translation messages from templates (Twig and PHP)
+ * added dumpers for translation catalogs
+ * added support for QT, gettext, and ResourceBundles
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Catalogue;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\LogicException;
+
+/**
+ * Base catalogues binary operation class.
+ *
+ * A catalogue binary operation performs operation on
+ * source (the left argument) and target (the right argument) catalogues.
+ *
+ * @author Jean-François Simon <contact@jfsimon.fr>
+ */
+abstract class AbstractOperation implements OperationInterface
+{
+ /**
+ * @var MessageCatalogueInterface The source catalogue
+ */
+ protected $source;
+
+ /**
+ * @var MessageCatalogueInterface The target catalogue
+ */
+ protected $target;
+
+ /**
+ * @var MessageCatalogue The result catalogue
+ */
+ protected $result;
+
+ /**
+ * @var null|array The domains affected by this operation
+ */
+ private $domains;
+
+ /**
+ * This array stores 'all', 'new' and 'obsolete' messages for all valid domains.
+ *
+ * The data structure of this array is as follows:
+ * ```php
+ * array(
+ * 'domain 1' => array(
+ * 'all' => array(...),
+ * 'new' => array(...),
+ * 'obsolete' => array(...)
+ * ),
+ * 'domain 2' => array(
+ * 'all' => array(...),
+ * 'new' => array(...),
+ * 'obsolete' => array(...)
+ * ),
+ * ...
+ * )
+ * ```
+ *
+ * @var array The array that stores 'all', 'new' and 'obsolete' messages
+ */
+ protected $messages;
+
+ /**
+ * @param MessageCatalogueInterface $source The source catalogue
+ * @param MessageCatalogueInterface $target The target catalogue
+ *
+ * @throws LogicException
+ */
+ public function __construct(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
+ {
+ if ($source->getLocale() !== $target->getLocale()) {
+ throw new LogicException('Operated catalogues must belong to the same locale.');
+ }
+
+ $this->source = $source;
+ $this->target = $target;
+ $this->result = new MessageCatalogue($source->getLocale());
+ $this->messages = array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDomains()
+ {
+ if (null === $this->domains) {
+ $this->domains = array_values(array_unique(array_merge($this->source->getDomains(), $this->target->getDomains())));
+ }
+
+ return $this->domains;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMessages($domain)
+ {
+ if (!in_array($domain, $this->getDomains())) {
+ throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
+ }
+
+ if (!isset($this->messages[$domain]['all'])) {
+ $this->processDomain($domain);
+ }
+
+ return $this->messages[$domain]['all'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getNewMessages($domain)
+ {
+ if (!in_array($domain, $this->getDomains())) {
+ throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
+ }
+
+ if (!isset($this->messages[$domain]['new'])) {
+ $this->processDomain($domain);
+ }
+
+ return $this->messages[$domain]['new'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getObsoleteMessages($domain)
+ {
+ if (!in_array($domain, $this->getDomains())) {
+ throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
+ }
+
+ if (!isset($this->messages[$domain]['obsolete'])) {
+ $this->processDomain($domain);
+ }
+
+ return $this->messages[$domain]['obsolete'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getResult()
+ {
+ foreach ($this->getDomains() as $domain) {
+ if (!isset($this->messages[$domain])) {
+ $this->processDomain($domain);
+ }
+ }
+
+ return $this->result;
+ }
+
+ /**
+ * Performs operation on source and target catalogues for the given domain and
+ * stores the results.
+ *
+ * @param string $domain The domain which the operation will be performed for
+ */
+ abstract protected function processDomain($domain);
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Catalogue;
+
+/**
+ * Merge operation between two catalogues as follows:
+ * all = source ∪ target = {x: x ∈ source ∨ x ∈ target}
+ * new = all ∖ source = {x: x ∈ target ∧ x ∉ source}
+ * obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ source ∧ x ∉ target} = ∅
+ * Basically, the result contains messages from both catalogues.
+ *
+ * @author Jean-François Simon <contact@jfsimon.fr>
+ */
+class MergeOperation extends AbstractOperation
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function processDomain($domain)
+ {
+ $this->messages[$domain] = array(
+ 'all' => array(),
+ 'new' => array(),
+ 'obsolete' => array(),
+ );
+
+ foreach ($this->source->all($domain) as $id => $message) {
+ $this->messages[$domain]['all'][$id] = $message;
+ $this->result->add(array($id => $message), $domain);
+ if (null !== $keyMetadata = $this->source->getMetadata($id, $domain)) {
+ $this->result->setMetadata($id, $keyMetadata, $domain);
+ }
+ }
+
+ foreach ($this->target->all($domain) as $id => $message) {
+ if (!$this->source->has($id, $domain)) {
+ $this->messages[$domain]['all'][$id] = $message;
+ $this->messages[$domain]['new'][$id] = $message;
+ $this->result->add(array($id => $message), $domain);
+ if (null !== $keyMetadata = $this->target->getMetadata($id, $domain)) {
+ $this->result->setMetadata($id, $keyMetadata, $domain);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Catalogue;
+
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+/**
+ * Represents an operation on catalogue(s).
+ *
+ * An instance of this interface performs an operation on one or more catalogues and
+ * stores intermediate and final results of the operation.
+ *
+ * The first catalogue in its argument(s) is called the 'source catalogue' or 'source' and
+ * the following results are stored:
+ *
+ * Messages: also called 'all', are valid messages for the given domain after the operation is performed.
+ *
+ * New Messages: also called 'new' (new = all ∖ source = {x: x ∈ all ∧ x ∉ source}).
+ *
+ * Obsolete Messages: also called 'obsolete' (obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ all}).
+ *
+ * Result: also called 'result', is the resulting catalogue for the given domain that holds the same messages as 'all'.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ */
+interface OperationInterface
+{
+ /**
+ * Returns domains affected by operation.
+ *
+ * @return array
+ */
+ public function getDomains();
+
+ /**
+ * Returns all valid messages ('all') after operation.
+ *
+ * @param string $domain
+ *
+ * @return array
+ */
+ public function getMessages($domain);
+
+ /**
+ * Returns new messages ('new') after operation.
+ *
+ * @param string $domain
+ *
+ * @return array
+ */
+ public function getNewMessages($domain);
+
+ /**
+ * Returns obsolete messages ('obsolete') after operation.
+ *
+ * @param string $domain
+ *
+ * @return array
+ */
+ public function getObsoleteMessages($domain);
+
+ /**
+ * Returns resulting catalogue ('result').
+ *
+ * @return MessageCatalogueInterface
+ */
+ public function getResult();
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Catalogue;
+
+/**
+ * Target operation between two catalogues:
+ * intersection = source ∩ target = {x: x ∈ source ∧ x ∈ target}
+ * all = intersection ∪ (target ∖ intersection) = target
+ * new = all ∖ source = {x: x ∈ target ∧ x ∉ source}
+ * obsolete = source ∖ all = source ∖ target = {x: x ∈ source ∧ x ∉ target}
+ * Basically, the result contains messages from the target catalogue.
+ *
+ * @author Michael Lee <michael.lee@zerustech.com>
+ */
+class TargetOperation extends AbstractOperation
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function processDomain($domain)
+ {
+ $this->messages[$domain] = array(
+ 'all' => array(),
+ 'new' => array(),
+ 'obsolete' => array(),
+ );
+
+ // For 'all' messages, the code can't be simplified as ``$this->messages[$domain]['all'] = $target->all($domain);``,
+ // because doing so will drop messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback}
+ //
+ // For 'new' messages, the code can't be simplied as ``array_diff_assoc($this->target->all($domain), $this->source->all($domain));``
+ // because doing so will not exclude messages like {x: x ∈ target ∧ x ∉ source.all ∧ x ∈ source.fallback}
+ //
+ // For 'obsolete' messages, the code can't be simplifed as ``array_diff_assoc($this->source->all($domain), $this->target->all($domain))``
+ // because doing so will not exclude messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback}
+
+ foreach ($this->source->all($domain) as $id => $message) {
+ if ($this->target->has($id, $domain)) {
+ $this->messages[$domain]['all'][$id] = $message;
+ $this->result->add(array($id => $message), $domain);
+ if (null !== $keyMetadata = $this->source->getMetadata($id, $domain)) {
+ $this->result->setMetadata($id, $keyMetadata, $domain);
+ }
+ } else {
+ $this->messages[$domain]['obsolete'][$id] = $message;
+ }
+ }
+
+ foreach ($this->target->all($domain) as $id => $message) {
+ if (!$this->source->has($id, $domain)) {
+ $this->messages[$domain]['all'][$id] = $message;
+ $this->messages[$domain]['new'][$id] = $message;
+ $this->result->add(array($id => $message), $domain);
+ if (null !== $keyMetadata = $this->target->getMetadata($id, $domain)) {
+ $this->result->setMetadata($id, $keyMetadata, $domain);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+/**
+ * Validates XLIFF files syntax and outputs encountered errors.
+ *
+ * @author Grégoire Pineau <lyrixx@lyrixx.info>
+ * @author Robin Chalas <robin.chalas@gmail.com>
+ * @author Javier Eguiluz <javier.eguiluz@gmail.com>
+ */
+class XliffLintCommand extends Command
+{
+ private $format;
+ private $displayCorrectFiles;
+ private $directoryIteratorProvider;
+ private $isReadableProvider;
+
+ public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null)
+ {
+ parent::__construct($name);
+
+ $this->directoryIteratorProvider = $directoryIteratorProvider;
+ $this->isReadableProvider = $isReadableProvider;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('lint:xliff')
+ ->setDescription('Lints a XLIFF file and outputs encountered errors')
+ ->addArgument('filename', null, 'A file or a directory or STDIN')
+ ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
+ ->setHelp(<<<EOF
+The <info>%command.name%</info> command lints a XLIFF file and outputs to STDOUT
+the first encountered syntax error.
+
+You can validates XLIFF contents passed from STDIN:
+
+ <info>cat filename | php %command.full_name%</info>
+
+You can also validate the syntax of a file:
+
+ <info>php %command.full_name% filename</info>
+
+Or of a whole directory:
+
+ <info>php %command.full_name% dirname</info>
+ <info>php %command.full_name% dirname --format=json</info>
+
+EOF
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $io = new SymfonyStyle($input, $output);
+ $filename = $input->getArgument('filename');
+ $this->format = $input->getOption('format');
+ $this->displayCorrectFiles = $output->isVerbose();
+
+ if (!$filename) {
+ if (!$stdin = $this->getStdin()) {
+ throw new \RuntimeException('Please provide a filename or pipe file content to STDIN.');
+ }
+
+ return $this->display($io, array($this->validate($stdin)));
+ }
+
+ if (!$this->isReadable($filename)) {
+ throw new \RuntimeException(sprintf('File or directory "%s" is not readable.', $filename));
+ }
+
+ $filesInfo = array();
+ foreach ($this->getFiles($filename) as $file) {
+ $filesInfo[] = $this->validate(file_get_contents($file), $file);
+ }
+
+ return $this->display($io, $filesInfo);
+ }
+
+ private function validate($content, $file = null)
+ {
+ // Avoid: Warning DOMDocument::loadXML(): Empty string supplied as input
+ if ('' === trim($content)) {
+ return array('file' => $file, 'valid' => true);
+ }
+
+ libxml_use_internal_errors(true);
+
+ $document = new \DOMDocument();
+ $document->loadXML($content);
+ if ($document->schemaValidate(__DIR__.'/../Resources/schemas/xliff-core-1.2-strict.xsd')) {
+ return array('file' => $file, 'valid' => true);
+ }
+
+ $errorMessages = array_map(function ($error) {
+ return array(
+ 'line' => $error->line,
+ 'column' => $error->column,
+ 'message' => trim($error->message),
+ );
+ }, libxml_get_errors());
+
+ libxml_clear_errors();
+ libxml_use_internal_errors(false);
+
+ return array('file' => $file, 'valid' => false, 'messages' => $errorMessages);
+ }
+
+ private function display(SymfonyStyle $io, array $files)
+ {
+ switch ($this->format) {
+ case 'txt':
+ return $this->displayTxt($io, $files);
+ case 'json':
+ return $this->displayJson($io, $files);
+ default:
+ throw new \InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format));
+ }
+ }
+
+ private function displayTxt(SymfonyStyle $io, array $filesInfo)
+ {
+ $countFiles = count($filesInfo);
+ $erroredFiles = 0;
+
+ foreach ($filesInfo as $info) {
+ if ($info['valid'] && $this->displayCorrectFiles) {
+ $io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
+ } elseif (!$info['valid']) {
+ ++$erroredFiles;
+ $io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
+ $io->listing(array_map(function ($error) {
+ // general document errors have a '-1' line number
+ return -1 === $error['line'] ? $error['message'] : sprintf('Line %d, Column %d: %s', $error['line'], $error['column'], $error['message']);
+ }, $info['messages']));
+ }
+ }
+
+ if (0 === $erroredFiles) {
+ $io->success(sprintf('All %d XLIFF files contain valid syntax.', $countFiles));
+ } else {
+ $io->warning(sprintf('%d XLIFF files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
+ }
+
+ return min($erroredFiles, 1);
+ }
+
+ private function displayJson(SymfonyStyle $io, array $filesInfo)
+ {
+ $errors = 0;
+
+ array_walk($filesInfo, function (&$v) use (&$errors) {
+ $v['file'] = (string) $v['file'];
+ if (!$v['valid']) {
+ ++$errors;
+ }
+ });
+
+ $io->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
+
+ return min($errors, 1);
+ }
+
+ private function getFiles($fileOrDirectory)
+ {
+ if (is_file($fileOrDirectory)) {
+ yield new \SplFileInfo($fileOrDirectory);
+
+ return;
+ }
+
+ foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
+ if (!in_array($file->getExtension(), array('xlf', 'xliff'))) {
+ continue;
+ }
+
+ yield $file;
+ }
+ }
+
+ private function getStdin()
+ {
+ if (0 !== ftell(STDIN)) {
+ return;
+ }
+
+ $inputs = '';
+ while (!feof(STDIN)) {
+ $inputs .= fread(STDIN, 1024);
+ }
+
+ return $inputs;
+ }
+
+ private function getDirectoryIterator($directory)
+ {
+ $default = function ($directory) {
+ return new \RecursiveIteratorIterator(
+ new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
+ \RecursiveIteratorIterator::LEAVES_ONLY
+ );
+ };
+
+ if (null !== $this->directoryIteratorProvider) {
+ return call_user_func($this->directoryIteratorProvider, $directory, $default);
+ }
+
+ return $default($directory);
+ }
+
+ private function isReadable($fileOrDirectory)
+ {
+ $default = function ($fileOrDirectory) {
+ return is_readable($fileOrDirectory);
+ };
+
+ if (null !== $this->isReadableProvider) {
+ return call_user_func($this->isReadableProvider, $fileOrDirectory, $default);
+ }
+
+ return $default($fileOrDirectory);
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\DataCollector;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\DataCollector\DataCollector;
+use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
+use Symfony\Component\Translation\DataCollectorTranslator;
+
+/**
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class TranslationDataCollector extends DataCollector implements LateDataCollectorInterface
+{
+ /**
+ * @var DataCollectorTranslator
+ */
+ private $translator;
+
+ /**
+ * @param DataCollectorTranslator $translator
+ */
+ public function __construct(DataCollectorTranslator $translator)
+ {
+ $this->translator = $translator;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function lateCollect()
+ {
+ $messages = $this->sanitizeCollectedMessages($this->translator->getCollectedMessages());
+
+ $this->data = $this->computeCount($messages);
+ $this->data['messages'] = $messages;
+
+ $this->data = $this->cloneVar($this->data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function collect(Request $request, Response $response, \Exception $exception = null)
+ {
+ }
+
+ /**
+ * @return array
+ */
+ public function getMessages()
+ {
+ return isset($this->data['messages']) ? $this->data['messages'] : array();
+ }
+
+ /**
+ * @return int
+ */
+ public function getCountMissings()
+ {
+ return isset($this->data[DataCollectorTranslator::MESSAGE_MISSING]) ? $this->data[DataCollectorTranslator::MESSAGE_MISSING] : 0;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCountFallbacks()
+ {
+ return isset($this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK]) ? $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] : 0;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCountDefines()
+ {
+ return isset($this->data[DataCollectorTranslator::MESSAGE_DEFINED]) ? $this->data[DataCollectorTranslator::MESSAGE_DEFINED] : 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return 'translation';
+ }
+
+ private function sanitizeCollectedMessages($messages)
+ {
+ $result = array();
+ foreach ($messages as $key => $message) {
+ $messageId = $message['locale'].$message['domain'].$message['id'];
+
+ if (!isset($result[$messageId])) {
+ $message['count'] = 1;
+ $message['parameters'] = !empty($message['parameters']) ? array($message['parameters']) : array();
+ $messages[$key]['translation'] = $this->sanitizeString($message['translation']);
+ $result[$messageId] = $message;
+ } else {
+ if (!empty($message['parameters'])) {
+ $result[$messageId]['parameters'][] = $message['parameters'];
+ }
+
+ ++$result[$messageId]['count'];
+ }
+
+ unset($messages[$key]);
+ }
+
+ return $result;
+ }
+
+ private function computeCount($messages)
+ {
+ $count = array(
+ DataCollectorTranslator::MESSAGE_DEFINED => 0,
+ DataCollectorTranslator::MESSAGE_MISSING => 0,
+ DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK => 0,
+ );
+
+ foreach ($messages as $message) {
+ ++$count[$message['state']];
+ }
+
+ return $count;
+ }
+
+ private function sanitizeString($string, $length = 80)
+ {
+ $string = trim(preg_replace('/\s+/', ' ', $string));
+
+ if (false !== $encoding = mb_detect_encoding($string, null, true)) {
+ if (mb_strlen($string, $encoding) > $length) {
+ return mb_substr($string, 0, $length - 3, $encoding).'...';
+ }
+ } elseif (strlen($string) > $length) {
+ return substr($string, 0, $length - 3).'...';
+ }
+
+ return $string;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInterface
+{
+ const MESSAGE_DEFINED = 0;
+ const MESSAGE_MISSING = 1;
+ const MESSAGE_EQUALS_FALLBACK = 2;
+
+ /**
+ * @var TranslatorInterface|TranslatorBagInterface
+ */
+ private $translator;
+
+ /**
+ * @var array
+ */
+ private $messages = array();
+
+ /**
+ * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface
+ */
+ public function __construct(TranslatorInterface $translator)
+ {
+ if (!$translator instanceof TranslatorBagInterface) {
+ throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', get_class($translator)));
+ }
+
+ $this->translator = $translator;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function trans($id, array $parameters = array(), $domain = null, $locale = null)
+ {
+ $trans = $this->translator->trans($id, $parameters, $domain, $locale);
+ $this->collectMessage($locale, $domain, $id, $trans, $parameters);
+
+ return $trans;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
+ {
+ $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
+ $this->collectMessage($locale, $domain, $id, $trans, $parameters, $number);
+
+ return $trans;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLocale($locale)
+ {
+ $this->translator->setLocale($locale);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLocale()
+ {
+ return $this->translator->getLocale();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCatalogue($locale = null)
+ {
+ return $this->translator->getCatalogue($locale);
+ }
+
+ /**
+ * Gets the fallback locales.
+ *
+ * @return array $locales The fallback locales
+ */
+ public function getFallbackLocales()
+ {
+ if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
+ return $this->translator->getFallbackLocales();
+ }
+
+ return array();
+ }
+
+ /**
+ * Passes through all unknown calls onto the translator object.
+ */
+ public function __call($method, $args)
+ {
+ return call_user_func_array(array($this->translator, $method), $args);
+ }
+
+ /**
+ * @return array
+ */
+ public function getCollectedMessages()
+ {
+ return $this->messages;
+ }
+
+ /**
+ * @param string|null $locale
+ * @param string|null $domain
+ * @param string $id
+ * @param string $translation
+ * @param array|null $parameters
+ * @param int|null $number
+ */
+ private function collectMessage($locale, $domain, $id, $translation, $parameters = array(), $number = null)
+ {
+ if (null === $domain) {
+ $domain = 'messages';
+ }
+
+ $id = (string) $id;
+ $catalogue = $this->translator->getCatalogue($locale);
+ $locale = $catalogue->getLocale();
+ if ($catalogue->defines($id, $domain)) {
+ $state = self::MESSAGE_DEFINED;
+ } elseif ($catalogue->has($id, $domain)) {
+ $state = self::MESSAGE_EQUALS_FALLBACK;
+
+ $fallbackCatalogue = $catalogue->getFallbackCatalogue();
+ while ($fallbackCatalogue) {
+ if ($fallbackCatalogue->defines($id, $domain)) {
+ $locale = $fallbackCatalogue->getLocale();
+ break;
+ }
+
+ $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue();
+ }
+ } else {
+ $state = self::MESSAGE_MISSING;
+ }
+
+ $this->messages[] = array(
+ 'locale' => $locale,
+ 'domain' => $domain,
+ 'id' => $id,
+ 'translation' => $translation,
+ 'parameters' => $parameters,
+ 'transChoiceNumber' => $number,
+ 'state' => $state,
+ );
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * CsvFileDumper generates a csv formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class CsvFileDumper extends FileDumper
+{
+ private $delimiter = ';';
+ private $enclosure = '"';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $handle = fopen('php://memory', 'rb+');
+
+ foreach ($messages->all($domain) as $source => $target) {
+ fputcsv($handle, array($source, $target), $this->delimiter, $this->enclosure);
+ }
+
+ rewind($handle);
+ $output = stream_get_contents($handle);
+ fclose($handle);
+
+ return $output;
+ }
+
+ /**
+ * Sets the delimiter and escape character for CSV.
+ *
+ * @param string $delimiter delimiter character
+ * @param string $enclosure enclosure character
+ */
+ public function setCsvControl($delimiter = ';', $enclosure = '"')
+ {
+ $this->delimiter = $delimiter;
+ $this->enclosure = $enclosure;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'csv';
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * DumperInterface is the interface implemented by all translation dumpers.
+ * There is no common option.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+interface DumperInterface
+{
+ /**
+ * Dumps the message catalogue.
+ *
+ * @param MessageCatalogue $messages The message catalogue
+ * @param array $options Options that are used by the dumper
+ */
+ public function dump(MessageCatalogue $messages, $options = array());
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\RuntimeException;
+
+/**
+ * FileDumper is an implementation of DumperInterface that dump a message catalogue to file(s).
+ * Performs backup of already existing files.
+ *
+ * Options:
+ * - path (mandatory): the directory where the files should be saved
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+abstract class FileDumper implements DumperInterface
+{
+ /**
+ * A template for the relative paths to files.
+ *
+ * @var string
+ */
+ protected $relativePathTemplate = '%domain%.%locale%.%extension%';
+
+ /**
+ * Make file backup before the dump.
+ *
+ * @var bool
+ */
+ private $backup = true;
+
+ /**
+ * Sets the template for the relative paths to files.
+ *
+ * @param string $relativePathTemplate A template for the relative paths to files
+ */
+ public function setRelativePathTemplate($relativePathTemplate)
+ {
+ $this->relativePathTemplate = $relativePathTemplate;
+ }
+
+ /**
+ * Sets backup flag.
+ *
+ * @param bool
+ */
+ public function setBackup($backup)
+ {
+ $this->backup = $backup;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function dump(MessageCatalogue $messages, $options = array())
+ {
+ if (!array_key_exists('path', $options)) {
+ throw new InvalidArgumentException('The file dumper needs a path option.');
+ }
+
+ // save a file for each domain
+ foreach ($messages->getDomains() as $domain) {
+ // backup
+ $fullpath = $options['path'].'/'.$this->getRelativePath($domain, $messages->getLocale());
+ if (file_exists($fullpath)) {
+ if ($this->backup) {
+ @trigger_error('Creating a backup while dumping a message catalogue is deprecated since version 3.1 and will be removed in 4.0. Use TranslationWriter::disableBackup() to disable the backup.', E_USER_DEPRECATED);
+ copy($fullpath, $fullpath.'~');
+ }
+ } else {
+ $directory = dirname($fullpath);
+ if (!file_exists($directory) && !@mkdir($directory, 0777, true)) {
+ throw new RuntimeException(sprintf('Unable to create directory "%s".', $directory));
+ }
+ }
+ // save file
+ file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
+ }
+ }
+
+ /**
+ * Transforms a domain of a message catalogue to its string representation.
+ *
+ * @param MessageCatalogue $messages
+ * @param string $domain
+ * @param array $options
+ *
+ * @return string representation
+ */
+ abstract public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array());
+
+ /**
+ * Gets the file extension of the dumper.
+ *
+ * @return string file extension
+ */
+ abstract protected function getExtension();
+
+ /**
+ * Gets the relative file path using the template.
+ *
+ * @param string $domain The domain
+ * @param string $locale The locale
+ *
+ * @return string The relative file path
+ */
+ private function getRelativePath($domain, $locale)
+ {
+ return strtr($this->relativePathTemplate, array(
+ '%domain%' => $domain,
+ '%locale%' => $locale,
+ '%extension%' => $this->getExtension(),
+ ));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IcuResDumper generates an ICU ResourceBundle formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class IcuResFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected $relativePathTemplate = '%domain%/%locale%.%extension%';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $data = $indexes = $resources = '';
+
+ foreach ($messages->all($domain) as $source => $target) {
+ $indexes .= pack('v', strlen($data) + 28);
+ $data .= $source."\0";
+ }
+
+ $data .= $this->writePadding($data);
+
+ $keyTop = $this->getPosition($data);
+
+ foreach ($messages->all($domain) as $source => $target) {
+ $resources .= pack('V', $this->getPosition($data));
+
+ $data .= pack('V', strlen($target))
+ .mb_convert_encoding($target."\0", 'UTF-16LE', 'UTF-8')
+ .$this->writePadding($data)
+ ;
+ }
+
+ $resOffset = $this->getPosition($data);
+
+ $data .= pack('v', count($messages->all($domain)))
+ .$indexes
+ .$this->writePadding($data)
+ .$resources
+ ;
+
+ $bundleTop = $this->getPosition($data);
+
+ $root = pack('V7',
+ $resOffset + (2 << 28), // Resource Offset + Resource Type
+ 6, // Index length
+ $keyTop, // Index keys top
+ $bundleTop, // Index resources top
+ $bundleTop, // Index bundle top
+ count($messages->all($domain)), // Index max table length
+ 0 // Index attributes
+ );
+
+ $header = pack('vC2v4C12@32',
+ 32, // Header size
+ 0xDA, 0x27, // Magic number 1 and 2
+ 20, 0, 0, 2, // Rest of the header, ..., Size of a char
+ 0x52, 0x65, 0x73, 0x42, // Data format identifier
+ 1, 2, 0, 0, // Data version
+ 1, 4, 0, 0 // Unicode version
+ );
+
+ return $header.$root.$data;
+ }
+
+ private function writePadding($data)
+ {
+ $padding = strlen($data) % 4;
+
+ if ($padding) {
+ return str_repeat("\xAA", 4 - $padding);
+ }
+ }
+
+ private function getPosition($data)
+ {
+ return (strlen($data) + 28) / 4;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'res';
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IniFileDumper generates an ini formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class IniFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $output = '';
+
+ foreach ($messages->all($domain) as $source => $target) {
+ $escapeTarget = str_replace('"', '\"', $target);
+ $output .= $source.'="'.$escapeTarget."\"\n";
+ }
+
+ return $output;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'ini';
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * JsonFileDumper generates an json formatted string representation of a message catalogue.
+ *
+ * @author singles
+ */
+class JsonFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ if (isset($options['json_encoding'])) {
+ $flags = $options['json_encoding'];
+ } else {
+ $flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0;
+ }
+
+ return json_encode($messages->all($domain), $flags);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'json';
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Loader\MoFileLoader;
+
+/**
+ * MoFileDumper generates a gettext formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class MoFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $sources = $targets = $sourceOffsets = $targetOffsets = '';
+ $offsets = array();
+ $size = 0;
+
+ foreach ($messages->all($domain) as $source => $target) {
+ $offsets[] = array_map('strlen', array($sources, $source, $targets, $target));
+ $sources .= "\0".$source;
+ $targets .= "\0".$target;
+ ++$size;
+ }
+
+ $header = array(
+ 'magicNumber' => MoFileLoader::MO_LITTLE_ENDIAN_MAGIC,
+ 'formatRevision' => 0,
+ 'count' => $size,
+ 'offsetId' => MoFileLoader::MO_HEADER_SIZE,
+ 'offsetTranslated' => MoFileLoader::MO_HEADER_SIZE + (8 * $size),
+ 'sizeHashes' => 0,
+ 'offsetHashes' => MoFileLoader::MO_HEADER_SIZE + (16 * $size),
+ );
+
+ $sourcesSize = strlen($sources);
+ $sourcesStart = $header['offsetHashes'] + 1;
+
+ foreach ($offsets as $offset) {
+ $sourceOffsets .= $this->writeLong($offset[1])
+ .$this->writeLong($offset[0] + $sourcesStart);
+ $targetOffsets .= $this->writeLong($offset[3])
+ .$this->writeLong($offset[2] + $sourcesStart + $sourcesSize);
+ }
+
+ $output = implode(array_map(array($this, 'writeLong'), $header))
+ .$sourceOffsets
+ .$targetOffsets
+ .$sources
+ .$targets
+ ;
+
+ return $output;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'mo';
+ }
+
+ private function writeLong($str)
+ {
+ return pack('V*', $str);
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * PhpFileDumper generates PHP files from a message catalogue.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class PhpFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ return "<?php\n\nreturn ".var_export($messages->all($domain), true).";\n";
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'php';
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * PoFileDumper generates a gettext formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class PoFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $output = 'msgid ""'."\n";
+ $output .= 'msgstr ""'."\n";
+ $output .= '"Content-Type: text/plain; charset=UTF-8\n"'."\n";
+ $output .= '"Content-Transfer-Encoding: 8bit\n"'."\n";
+ $output .= '"Language: '.$messages->getLocale().'\n"'."\n";
+ $output .= "\n";
+
+ $newLine = false;
+ foreach ($messages->all($domain) as $source => $target) {
+ if ($newLine) {
+ $output .= "\n";
+ } else {
+ $newLine = true;
+ }
+ $output .= sprintf('msgid "%s"'."\n", $this->escape($source));
+ $output .= sprintf('msgstr "%s"', $this->escape($target));
+ }
+
+ return $output;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'po';
+ }
+
+ private function escape($str)
+ {
+ return addcslashes($str, "\0..\37\42\134");
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * QtFileDumper generates ts files from a message catalogue.
+ *
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class QtFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $dom = new \DOMDocument('1.0', 'utf-8');
+ $dom->formatOutput = true;
+ $ts = $dom->appendChild($dom->createElement('TS'));
+ $context = $ts->appendChild($dom->createElement('context'));
+ $context->appendChild($dom->createElement('name', $domain));
+
+ foreach ($messages->all($domain) as $source => $target) {
+ $message = $context->appendChild($dom->createElement('message'));
+ $message->appendChild($dom->createElement('source', $source));
+ $message->appendChild($dom->createElement('translation', $target));
+ }
+
+ return $dom->saveXML();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'ts';
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * XliffFileDumper generates xliff files from a message catalogue.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class XliffFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $xliffVersion = '1.2';
+ if (array_key_exists('xliff_version', $options)) {
+ $xliffVersion = $options['xliff_version'];
+ }
+
+ if (array_key_exists('default_locale', $options)) {
+ $defaultLocale = $options['default_locale'];
+ } else {
+ $defaultLocale = \Locale::getDefault();
+ }
+
+ if ('1.2' === $xliffVersion) {
+ return $this->dumpXliff1($defaultLocale, $messages, $domain, $options);
+ }
+ if ('2.0' === $xliffVersion) {
+ return $this->dumpXliff2($defaultLocale, $messages, $domain, $options);
+ }
+
+ throw new InvalidArgumentException(sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'xlf';
+ }
+
+ private function dumpXliff1($defaultLocale, MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $toolInfo = array('tool-id' => 'symfony', 'tool-name' => 'Symfony');
+ if (array_key_exists('tool_info', $options)) {
+ $toolInfo = array_merge($toolInfo, $options['tool_info']);
+ }
+
+ $dom = new \DOMDocument('1.0', 'utf-8');
+ $dom->formatOutput = true;
+
+ $xliff = $dom->appendChild($dom->createElement('xliff'));
+ $xliff->setAttribute('version', '1.2');
+ $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
+
+ $xliffFile = $xliff->appendChild($dom->createElement('file'));
+ $xliffFile->setAttribute('source-language', str_replace('_', '-', $defaultLocale));
+ $xliffFile->setAttribute('target-language', str_replace('_', '-', $messages->getLocale()));
+ $xliffFile->setAttribute('datatype', 'plaintext');
+ $xliffFile->setAttribute('original', 'file.ext');
+
+ $xliffHead = $xliffFile->appendChild($dom->createElement('header'));
+ $xliffTool = $xliffHead->appendChild($dom->createElement('tool'));
+ foreach ($toolInfo as $id => $value) {
+ $xliffTool->setAttribute($id, $value);
+ }
+
+ $xliffBody = $xliffFile->appendChild($dom->createElement('body'));
+ foreach ($messages->all($domain) as $source => $target) {
+ $translation = $dom->createElement('trans-unit');
+
+ $translation->setAttribute('id', md5($source));
+ $translation->setAttribute('resname', $source);
+
+ $s = $translation->appendChild($dom->createElement('source'));
+ $s->appendChild($dom->createTextNode($source));
+
+ // Does the target contain characters requiring a CDATA section?
+ $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target);
+
+ $targetElement = $dom->createElement('target');
+ $metadata = $messages->getMetadata($source, $domain);
+ if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) {
+ foreach ($metadata['target-attributes'] as $name => $value) {
+ $targetElement->setAttribute($name, $value);
+ }
+ }
+ $t = $translation->appendChild($targetElement);
+ $t->appendChild($text);
+
+ if ($this->hasMetadataArrayInfo('notes', $metadata)) {
+ foreach ($metadata['notes'] as $note) {
+ if (!isset($note['content'])) {
+ continue;
+ }
+
+ $n = $translation->appendChild($dom->createElement('note'));
+ $n->appendChild($dom->createTextNode($note['content']));
+
+ if (isset($note['priority'])) {
+ $n->setAttribute('priority', $note['priority']);
+ }
+
+ if (isset($note['from'])) {
+ $n->setAttribute('from', $note['from']);
+ }
+ }
+ }
+
+ $xliffBody->appendChild($translation);
+ }
+
+ return $dom->saveXML();
+ }
+
+ private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain, array $options = array())
+ {
+ $dom = new \DOMDocument('1.0', 'utf-8');
+ $dom->formatOutput = true;
+
+ $xliff = $dom->appendChild($dom->createElement('xliff'));
+ $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:2.0');
+ $xliff->setAttribute('version', '2.0');
+ $xliff->setAttribute('srcLang', str_replace('_', '-', $defaultLocale));
+ $xliff->setAttribute('trgLang', str_replace('_', '-', $messages->getLocale()));
+
+ $xliffFile = $xliff->appendChild($dom->createElement('file'));
+ $xliffFile->setAttribute('id', $domain.'.'.$messages->getLocale());
+
+ foreach ($messages->all($domain) as $source => $target) {
+ $translation = $dom->createElement('unit');
+ $translation->setAttribute('id', md5($source));
+
+ $segment = $translation->appendChild($dom->createElement('segment'));
+
+ $s = $segment->appendChild($dom->createElement('source'));
+ $s->appendChild($dom->createTextNode($source));
+
+ // Does the target contain characters requiring a CDATA section?
+ $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target);
+
+ $targetElement = $dom->createElement('target');
+ $metadata = $messages->getMetadata($source, $domain);
+ if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) {
+ foreach ($metadata['target-attributes'] as $name => $value) {
+ $targetElement->setAttribute($name, $value);
+ }
+ }
+ $t = $segment->appendChild($targetElement);
+ $t->appendChild($text);
+
+ $xliffFile->appendChild($translation);
+ }
+
+ return $dom->saveXML();
+ }
+
+ /**
+ * @param string $key
+ * @param array|null $metadata
+ *
+ * @return bool
+ */
+ private function hasMetadataArrayInfo($key, $metadata = null)
+ {
+ return null !== $metadata && array_key_exists($key, $metadata) && ($metadata[$key] instanceof \Traversable || is_array($metadata[$key]));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Util\ArrayConverter;
+use Symfony\Component\Yaml\Yaml;
+use Symfony\Component\Translation\Exception\LogicException;
+
+/**
+ * YamlFileDumper generates yaml files from a message catalogue.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class YamlFileDumper extends FileDumper
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ if (!class_exists('Symfony\Component\Yaml\Yaml')) {
+ throw new LogicException('Dumping translations in the YAML format requires the Symfony Yaml component.');
+ }
+
+ $data = $messages->all($domain);
+
+ if (isset($options['as_tree']) && $options['as_tree']) {
+ $data = ArrayConverter::expandToTree($data);
+ }
+
+ if (isset($options['inline']) && ($inline = (int) $options['inline']) > 0) {
+ return Yaml::dump($data, $inline);
+ }
+
+ return Yaml::dump($data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExtension()
+ {
+ return 'yml';
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Exception interface for all exceptions thrown by the component.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface ExceptionInterface
+{
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Base InvalidArgumentException for the Translation component.
+ *
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Thrown when a resource cannot be loaded.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class InvalidResourceException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Base LogicException for Translation component.
+ *
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class LogicException extends \LogicException implements ExceptionInterface
+{
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Thrown when a resource does not exist.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class NotFoundResourceException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Base RuntimeException for the Translation component.
+ *
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Extractor;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * Base class used by classes that extract translation messages from files.
+ *
+ * @author Marcos D. Sánchez <marcosdsanchez@gmail.com>
+ */
+abstract class AbstractFileExtractor
+{
+ /**
+ * @param string|array $resource files, a file or a directory
+ *
+ * @return array
+ */
+ protected function extractFiles($resource)
+ {
+ if (is_array($resource) || $resource instanceof \Traversable) {
+ $files = array();
+ foreach ($resource as $file) {
+ if ($this->canBeExtracted($file)) {
+ $files[] = $this->toSplFileInfo($file);
+ }
+ }
+ } elseif (is_file($resource)) {
+ $files = $this->canBeExtracted($resource) ? array($this->toSplFileInfo($resource)) : array();
+ } else {
+ $files = $this->extractFromDirectory($resource);
+ }
+
+ return $files;
+ }
+
+ /**
+ * @param string $file
+ *
+ * @return \SplFileInfo
+ */
+ private function toSplFileInfo($file)
+ {
+ return ($file instanceof \SplFileInfo) ? $file : new \SplFileInfo($file);
+ }
+
+ /**
+ * @param string $file
+ *
+ * @return bool
+ *
+ * @throws InvalidArgumentException
+ */
+ protected function isFile($file)
+ {
+ if (!is_file($file)) {
+ throw new InvalidArgumentException(sprintf('The "%s" file does not exist.', $file));
+ }
+
+ return true;
+ }
+
+ /**
+ * @param string $file
+ *
+ * @return bool
+ */
+ abstract protected function canBeExtracted($file);
+
+ /**
+ * @param string|array $resource files, a file or a directory
+ *
+ * @return array files to be extracted
+ */
+ abstract protected function extractFromDirectory($resource);
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Extractor;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * ChainExtractor extracts translation messages from template files.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class ChainExtractor implements ExtractorInterface
+{
+ /**
+ * The extractors.
+ *
+ * @var ExtractorInterface[]
+ */
+ private $extractors = array();
+
+ /**
+ * Adds a loader to the translation extractor.
+ *
+ * @param string $format The format of the loader
+ * @param ExtractorInterface $extractor The loader
+ */
+ public function addExtractor($format, ExtractorInterface $extractor)
+ {
+ $this->extractors[$format] = $extractor;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setPrefix($prefix)
+ {
+ foreach ($this->extractors as $extractor) {
+ $extractor->setPrefix($prefix);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function extract($directory, MessageCatalogue $catalogue)
+ {
+ foreach ($this->extractors as $extractor) {
+ $extractor->extract($directory, $catalogue);
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Extractor;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * Extracts translation messages from a directory or files to the catalogue.
+ * New found messages are injected to the catalogue using the prefix.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+interface ExtractorInterface
+{
+ /**
+ * Extracts translation messages from files, a file or a directory to the catalogue.
+ *
+ * @param string|array $resource files, a file or a directory
+ * @param MessageCatalogue $catalogue The catalogue
+ */
+ public function extract($resource, MessageCatalogue $catalogue);
+
+ /**
+ * Sets the prefix that should be used for new found messages.
+ *
+ * @param string $prefix The prefix
+ */
+ public function setPrefix($prefix);
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+/**
+ * IdentityTranslator does not translate anything.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class IdentityTranslator implements TranslatorInterface
+{
+ private $selector;
+ private $locale;
+
+ /**
+ * @param MessageSelector|null $selector The message selector for pluralization
+ */
+ public function __construct(MessageSelector $selector = null)
+ {
+ $this->selector = $selector ?: new MessageSelector();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLocale($locale)
+ {
+ $this->locale = $locale;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLocale()
+ {
+ return $this->locale ?: \Locale::getDefault();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function trans($id, array $parameters = array(), $domain = null, $locale = null)
+ {
+ return strtr((string) $id, $parameters);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
+ {
+ return strtr($this->selector->choose((string) $id, (int) $number, $locale ?: $this->getLocale()), $parameters);
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * Tests if a given number belongs to a given math interval.
+ *
+ * An interval can represent a finite set of numbers:
+ *
+ * {1,2,3,4}
+ *
+ * An interval can represent numbers between two numbers:
+ *
+ * [1, +Inf]
+ * ]-1,2[
+ *
+ * The left delimiter can be [ (inclusive) or ] (exclusive).
+ * The right delimiter can be [ (exclusive) or ] (inclusive).
+ * Beside numbers, you can use -Inf and +Inf for the infinite.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @see http://en.wikipedia.org/wiki/Interval_%28mathematics%29#The_ISO_notation
+ */
+class Interval
+{
+ /**
+ * Tests if the given number is in the math interval.
+ *
+ * @param int $number A number
+ * @param string $interval An interval
+ *
+ * @return bool
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function test($number, $interval)
+ {
+ $interval = trim($interval);
+
+ if (!preg_match('/^'.self::getIntervalRegexp().'$/x', $interval, $matches)) {
+ throw new InvalidArgumentException(sprintf('"%s" is not a valid interval.', $interval));
+ }
+
+ if ($matches[1]) {
+ foreach (explode(',', $matches[2]) as $n) {
+ if ($number == $n) {
+ return true;
+ }
+ }
+ } else {
+ $leftNumber = self::convertNumber($matches['left']);
+ $rightNumber = self::convertNumber($matches['right']);
+
+ return
+ ('[' === $matches['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber)
+ && (']' === $matches['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber)
+ ;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a Regexp that matches valid intervals.
+ *
+ * @return string A Regexp (without the delimiters)
+ */
+ public static function getIntervalRegexp()
+ {
+ return <<<EOF
+ ({\s*
+ (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)
+ \s*})
+
+ |
+
+ (?P<left_delimiter>[\[\]])
+ \s*
+ (?P<left>-Inf|\-?\d+(\.\d+)?)
+ \s*,\s*
+ (?P<right>\+?Inf|\-?\d+(\.\d+)?)
+ \s*
+ (?P<right_delimiter>[\[\]])
+EOF;
+ }
+
+ private static function convertNumber($number)
+ {
+ if ('-Inf' === $number) {
+ return log(0);
+ } elseif ('+Inf' === $number || 'Inf' === $number) {
+ return -log(0);
+ }
+
+ return (float) $number;
+ }
+}
--- /dev/null
+Copyright (c) 2004-2017 Fabien Potencier
+
+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.
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * ArrayLoader loads translations from a PHP array.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ArrayLoader implements LoaderInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($resource, $locale, $domain = 'messages')
+ {
+ $this->flatten($resource);
+ $catalogue = new MessageCatalogue($locale);
+ $catalogue->add($resource, $domain);
+
+ return $catalogue;
+ }
+
+ /**
+ * Flattens an nested array of translations.
+ *
+ * The scheme used is:
+ * 'key' => array('key2' => array('key3' => 'value'))
+ * Becomes:
+ * 'key.key2.key3' => 'value'
+ *
+ * This function takes an array by reference and will modify it
+ *
+ * @param array &$messages The array that will be flattened
+ * @param array $subnode Current subnode being parsed, used internally for recursive calls
+ * @param string $path Current path being parsed, used internally for recursive calls
+ */
+ private function flatten(array &$messages, array $subnode = null, $path = null)
+ {
+ if (null === $subnode) {
+ $subnode = &$messages;
+ }
+ foreach ($subnode as $key => $value) {
+ if (is_array($value)) {
+ $nodePath = $path ? $path.'.'.$key : $key;
+ $this->flatten($messages, $value, $nodePath);
+ if (null === $path) {
+ unset($messages[$key]);
+ }
+ } elseif (null !== $path) {
+ $messages[$path.'.'.$key] = $value;
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+
+/**
+ * CsvFileLoader loads translations from CSV files.
+ *
+ * @author Saša Stamenković <umpirsky@gmail.com>
+ */
+class CsvFileLoader extends FileLoader
+{
+ private $delimiter = ';';
+ private $enclosure = '"';
+ private $escape = '\\';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ $messages = array();
+
+ try {
+ $file = new \SplFileObject($resource, 'rb');
+ } catch (\RuntimeException $e) {
+ throw new NotFoundResourceException(sprintf('Error opening file "%s".', $resource), 0, $e);
+ }
+
+ $file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY);
+ $file->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
+
+ foreach ($file as $data) {
+ if ('#' !== substr($data[0], 0, 1) && isset($data[1]) && 2 === count($data)) {
+ $messages[$data[0]] = $data[1];
+ }
+ }
+
+ return $messages;
+ }
+
+ /**
+ * Sets the delimiter, enclosure, and escape character for CSV.
+ *
+ * @param string $delimiter delimiter character
+ * @param string $enclosure enclosure character
+ * @param string $escape escape character
+ */
+ public function setCsvControl($delimiter = ';', $enclosure = '"', $escape = '\\')
+ {
+ $this->delimiter = $delimiter;
+ $this->enclosure = $enclosure;
+ $this->escape = $escape;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+abstract class FileLoader extends ArrayLoader
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($resource, $locale, $domain = 'messages')
+ {
+ if (!stream_is_local($resource)) {
+ throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+ }
+
+ if (!file_exists($resource)) {
+ throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+ }
+
+ $messages = $this->loadResource($resource);
+
+ // empty resource
+ if (null === $messages) {
+ $messages = array();
+ }
+
+ // not an array
+ if (!is_array($messages)) {
+ throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource));
+ }
+
+ $catalogue = parent::load($messages, $locale, $domain);
+
+ if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
+ $catalogue->addResource(new FileResource($resource));
+ }
+
+ return $catalogue;
+ }
+
+ /**
+ * @param string $resource
+ *
+ * @return array
+ *
+ * @throws InvalidResourceException if stream content has an invalid format
+ */
+ abstract protected function loadResource($resource);
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * IcuResFileLoader loads translations from a resource bundle.
+ *
+ * @author stealth35
+ */
+class IcuDatFileLoader extends IcuResFileLoader
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($resource, $locale, $domain = 'messages')
+ {
+ if (!stream_is_local($resource.'.dat')) {
+ throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+ }
+
+ if (!file_exists($resource.'.dat')) {
+ throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+ }
+
+ try {
+ $rb = new \ResourceBundle($locale, $resource);
+ } catch (\Exception $e) {
+ // HHVM compatibility: constructor throws on invalid resource
+ $rb = null;
+ }
+
+ if (!$rb) {
+ throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource));
+ } elseif (intl_is_failure($rb->getErrorCode())) {
+ throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
+ }
+
+ $messages = $this->flatten($rb);
+ $catalogue = new MessageCatalogue($locale);
+ $catalogue->add($messages, $domain);
+
+ if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
+ $catalogue->addResource(new FileResource($resource.'.dat'));
+ }
+
+ return $catalogue;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\DirectoryResource;
+
+/**
+ * IcuResFileLoader loads translations from a resource bundle.
+ *
+ * @author stealth35
+ */
+class IcuResFileLoader implements LoaderInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($resource, $locale, $domain = 'messages')
+ {
+ if (!stream_is_local($resource)) {
+ throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+ }
+
+ if (!is_dir($resource)) {
+ throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+ }
+
+ try {
+ $rb = new \ResourceBundle($locale, $resource);
+ } catch (\Exception $e) {
+ // HHVM compatibility: constructor throws on invalid resource
+ $rb = null;
+ }
+
+ if (!$rb) {
+ throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource));
+ } elseif (intl_is_failure($rb->getErrorCode())) {
+ throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
+ }
+
+ $messages = $this->flatten($rb);
+ $catalogue = new MessageCatalogue($locale);
+ $catalogue->add($messages, $domain);
+
+ if (class_exists('Symfony\Component\Config\Resource\DirectoryResource')) {
+ $catalogue->addResource(new DirectoryResource($resource));
+ }
+
+ return $catalogue;
+ }
+
+ /**
+ * Flattens an ResourceBundle.
+ *
+ * The scheme used is:
+ * key { key2 { key3 { "value" } } }
+ * Becomes:
+ * 'key.key2.key3' => 'value'
+ *
+ * This function takes an array by reference and will modify it
+ *
+ * @param \ResourceBundle $rb the ResourceBundle that will be flattened
+ * @param array $messages used internally for recursive calls
+ * @param string $path current path being parsed, used internally for recursive calls
+ *
+ * @return array the flattened ResourceBundle
+ */
+ protected function flatten(\ResourceBundle $rb, array &$messages = array(), $path = null)
+ {
+ foreach ($rb as $key => $value) {
+ $nodePath = $path ? $path.'.'.$key : $key;
+ if ($value instanceof \ResourceBundle) {
+ $this->flatten($value, $messages, $nodePath);
+ } else {
+ $messages[$nodePath] = $value;
+ }
+ }
+
+ return $messages;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+/**
+ * IniFileLoader loads translations from an ini file.
+ *
+ * @author stealth35
+ */
+class IniFileLoader extends FileLoader
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ return parse_ini_file($resource, true);
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+
+/**
+ * JsonFileLoader loads translations from an json file.
+ *
+ * @author singles
+ */
+class JsonFileLoader extends FileLoader
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ $messages = array();
+ if ($data = file_get_contents($resource)) {
+ $messages = json_decode($data, true);
+
+ if (0 < $errorCode = json_last_error()) {
+ throw new InvalidResourceException(sprintf('Error parsing JSON - %s', $this->getJSONErrorMessage($errorCode)));
+ }
+ }
+
+ return $messages;
+ }
+
+ /**
+ * Translates JSON_ERROR_* constant into meaningful message.
+ *
+ * @param int $errorCode Error code returned by json_last_error() call
+ *
+ * @return string Message string
+ */
+ private function getJSONErrorMessage($errorCode)
+ {
+ switch ($errorCode) {
+ case JSON_ERROR_DEPTH:
+ return 'Maximum stack depth exceeded';
+ case JSON_ERROR_STATE_MISMATCH:
+ return 'Underflow or the modes mismatch';
+ case JSON_ERROR_CTRL_CHAR:
+ return 'Unexpected control character found';
+ case JSON_ERROR_SYNTAX:
+ return 'Syntax error, malformed JSON';
+ case JSON_ERROR_UTF8:
+ return 'Malformed UTF-8 characters, possibly incorrectly encoded';
+ default:
+ return 'Unknown error';
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+
+/**
+ * LoaderInterface is the interface implemented by all translation loaders.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface LoaderInterface
+{
+ /**
+ * Loads a locale.
+ *
+ * @param mixed $resource A resource
+ * @param string $locale A locale
+ * @param string $domain The domain
+ *
+ * @return MessageCatalogue A MessageCatalogue instance
+ *
+ * @throws NotFoundResourceException when the resource cannot be found
+ * @throws InvalidResourceException when the resource cannot be loaded
+ */
+ public function load($resource, $locale, $domain = 'messages');
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+
+/**
+ * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
+ */
+class MoFileLoader extends FileLoader
+{
+ /**
+ * Magic used for validating the format of a MO file as well as
+ * detecting if the machine used to create that file was little endian.
+ *
+ * @var float
+ */
+ const MO_LITTLE_ENDIAN_MAGIC = 0x950412de;
+
+ /**
+ * Magic used for validating the format of a MO file as well as
+ * detecting if the machine used to create that file was big endian.
+ *
+ * @var float
+ */
+ const MO_BIG_ENDIAN_MAGIC = 0xde120495;
+
+ /**
+ * The size of the header of a MO file in bytes.
+ *
+ * @var int Number of bytes
+ */
+ const MO_HEADER_SIZE = 28;
+
+ /**
+ * Parses machine object (MO) format, independent of the machine's endian it
+ * was created on. Both 32bit and 64bit systems are supported.
+ *
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ $stream = fopen($resource, 'r');
+
+ $stat = fstat($stream);
+
+ if ($stat['size'] < self::MO_HEADER_SIZE) {
+ throw new InvalidResourceException('MO stream content has an invalid format.');
+ }
+ $magic = unpack('V1', fread($stream, 4));
+ $magic = hexdec(substr(dechex(current($magic)), -8));
+
+ if (self::MO_LITTLE_ENDIAN_MAGIC == $magic) {
+ $isBigEndian = false;
+ } elseif (self::MO_BIG_ENDIAN_MAGIC == $magic) {
+ $isBigEndian = true;
+ } else {
+ throw new InvalidResourceException('MO stream content has an invalid format.');
+ }
+
+ // formatRevision
+ $this->readLong($stream, $isBigEndian);
+ $count = $this->readLong($stream, $isBigEndian);
+ $offsetId = $this->readLong($stream, $isBigEndian);
+ $offsetTranslated = $this->readLong($stream, $isBigEndian);
+ // sizeHashes
+ $this->readLong($stream, $isBigEndian);
+ // offsetHashes
+ $this->readLong($stream, $isBigEndian);
+
+ $messages = array();
+
+ for ($i = 0; $i < $count; ++$i) {
+ $pluralId = null;
+ $translated = null;
+
+ fseek($stream, $offsetId + $i * 8);
+
+ $length = $this->readLong($stream, $isBigEndian);
+ $offset = $this->readLong($stream, $isBigEndian);
+
+ if ($length < 1) {
+ continue;
+ }
+
+ fseek($stream, $offset);
+ $singularId = fread($stream, $length);
+
+ if (false !== strpos($singularId, "\000")) {
+ list($singularId, $pluralId) = explode("\000", $singularId);
+ }
+
+ fseek($stream, $offsetTranslated + $i * 8);
+ $length = $this->readLong($stream, $isBigEndian);
+ $offset = $this->readLong($stream, $isBigEndian);
+
+ if ($length < 1) {
+ continue;
+ }
+
+ fseek($stream, $offset);
+ $translated = fread($stream, $length);
+
+ if (false !== strpos($translated, "\000")) {
+ $translated = explode("\000", $translated);
+ }
+
+ $ids = array('singular' => $singularId, 'plural' => $pluralId);
+ $item = compact('ids', 'translated');
+
+ if (is_array($item['translated'])) {
+ $messages[$item['ids']['singular']] = stripcslashes($item['translated'][0]);
+ if (isset($item['ids']['plural'])) {
+ $plurals = array();
+ foreach ($item['translated'] as $plural => $translated) {
+ $plurals[] = sprintf('{%d} %s', $plural, $translated);
+ }
+ $messages[$item['ids']['plural']] = stripcslashes(implode('|', $plurals));
+ }
+ } elseif (!empty($item['ids']['singular'])) {
+ $messages[$item['ids']['singular']] = stripcslashes($item['translated']);
+ }
+ }
+
+ fclose($stream);
+
+ return array_filter($messages);
+ }
+
+ /**
+ * Reads an unsigned long from stream respecting endianness.
+ *
+ * @param resource $stream
+ * @param bool $isBigEndian
+ *
+ * @return int
+ */
+ private function readLong($stream, $isBigEndian)
+ {
+ $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4));
+ $result = current($result);
+
+ return (int) substr($result, -8);
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+/**
+ * PhpFileLoader loads translations from PHP files returning an array of translations.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class PhpFileLoader extends FileLoader
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ return require $resource;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+/**
+ * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
+ * @copyright Copyright (c) 2012, Clemens Tolboom
+ */
+class PoFileLoader extends FileLoader
+{
+ /**
+ * Parses portable object (PO) format.
+ *
+ * From http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files
+ * we should be able to parse files having:
+ *
+ * white-space
+ * # translator-comments
+ * #. extracted-comments
+ * #: reference...
+ * #, flag...
+ * #| msgid previous-untranslated-string
+ * msgid untranslated-string
+ * msgstr translated-string
+ *
+ * extra or different lines are:
+ *
+ * #| msgctxt previous-context
+ * #| msgid previous-untranslated-string
+ * msgctxt context
+ *
+ * #| msgid previous-untranslated-string-singular
+ * #| msgid_plural previous-untranslated-string-plural
+ * msgid untranslated-string-singular
+ * msgid_plural untranslated-string-plural
+ * msgstr[0] translated-string-case-0
+ * ...
+ * msgstr[N] translated-string-case-n
+ *
+ * The definition states:
+ * - white-space and comments are optional.
+ * - msgid "" that an empty singleline defines a header.
+ *
+ * This parser sacrifices some features of the reference implementation the
+ * differences to that implementation are as follows.
+ * - No support for comments spanning multiple lines.
+ * - Translator and extracted comments are treated as being the same type.
+ * - Message IDs are allowed to have other encodings as just US-ASCII.
+ *
+ * Items with an empty id are ignored.
+ *
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ $stream = fopen($resource, 'r');
+
+ $defaults = array(
+ 'ids' => array(),
+ 'translated' => null,
+ );
+
+ $messages = array();
+ $item = $defaults;
+ $flags = array();
+
+ while ($line = fgets($stream)) {
+ $line = trim($line);
+
+ if ('' === $line) {
+ // Whitespace indicated current item is done
+ if (!in_array('fuzzy', $flags)) {
+ $this->addMessage($messages, $item);
+ }
+ $item = $defaults;
+ $flags = array();
+ } elseif ('#,' === substr($line, 0, 2)) {
+ $flags = array_map('trim', explode(',', substr($line, 2)));
+ } elseif ('msgid "' === substr($line, 0, 7)) {
+ // We start a new msg so save previous
+ // TODO: this fails when comments or contexts are added
+ $this->addMessage($messages, $item);
+ $item = $defaults;
+ $item['ids']['singular'] = substr($line, 7, -1);
+ } elseif ('msgstr "' === substr($line, 0, 8)) {
+ $item['translated'] = substr($line, 8, -1);
+ } elseif ('"' === $line[0]) {
+ $continues = isset($item['translated']) ? 'translated' : 'ids';
+
+ if (is_array($item[$continues])) {
+ end($item[$continues]);
+ $item[$continues][key($item[$continues])] .= substr($line, 1, -1);
+ } else {
+ $item[$continues] .= substr($line, 1, -1);
+ }
+ } elseif ('msgid_plural "' === substr($line, 0, 14)) {
+ $item['ids']['plural'] = substr($line, 14, -1);
+ } elseif ('msgstr[' === substr($line, 0, 7)) {
+ $size = strpos($line, ']');
+ $item['translated'][(int) substr($line, 7, 1)] = substr($line, $size + 3, -1);
+ }
+ }
+ // save last item
+ if (!in_array('fuzzy', $flags)) {
+ $this->addMessage($messages, $item);
+ }
+ fclose($stream);
+
+ return $messages;
+ }
+
+ /**
+ * Save a translation item to the messages.
+ *
+ * A .po file could contain by error missing plural indexes. We need to
+ * fix these before saving them.
+ *
+ * @param array $messages
+ * @param array $item
+ */
+ private function addMessage(array &$messages, array $item)
+ {
+ if (is_array($item['translated'])) {
+ $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated'][0]);
+ if (isset($item['ids']['plural'])) {
+ $plurals = $item['translated'];
+ // PO are by definition indexed so sort by index.
+ ksort($plurals);
+ // Make sure every index is filled.
+ end($plurals);
+ $count = key($plurals);
+ // Fill missing spots with '-'.
+ $empties = array_fill(0, $count + 1, '-');
+ $plurals += $empties;
+ ksort($plurals);
+ $messages[stripcslashes($item['ids']['plural'])] = stripcslashes(implode('|', $plurals));
+ }
+ } elseif (!empty($item['ids']['singular'])) {
+ $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated']);
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Config\Util\XmlUtils;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * QtFileLoader loads translations from QT Translations XML files.
+ *
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class QtFileLoader implements LoaderInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($resource, $locale, $domain = 'messages')
+ {
+ if (!stream_is_local($resource)) {
+ throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+ }
+
+ if (!file_exists($resource)) {
+ throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+ }
+
+ try {
+ $dom = XmlUtils::loadFile($resource);
+ } catch (\InvalidArgumentException $e) {
+ throw new InvalidResourceException(sprintf('Unable to load "%s".', $resource), $e->getCode(), $e);
+ }
+
+ $internalErrors = libxml_use_internal_errors(true);
+ libxml_clear_errors();
+
+ $xpath = new \DOMXPath($dom);
+ $nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]');
+
+ $catalogue = new MessageCatalogue($locale);
+ if (1 == $nodes->length) {
+ $translations = $nodes->item(0)->nextSibling->parentNode->parentNode->getElementsByTagName('message');
+ foreach ($translations as $translation) {
+ $translationValue = (string) $translation->getElementsByTagName('translation')->item(0)->nodeValue;
+
+ if (!empty($translationValue)) {
+ $catalogue->set(
+ (string) $translation->getElementsByTagName('source')->item(0)->nodeValue,
+ $translationValue,
+ $domain
+ );
+ }
+ $translation = $translation->nextSibling;
+ }
+
+ if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
+ $catalogue->addResource(new FileResource($resource));
+ }
+ }
+
+ libxml_use_internal_errors($internalErrors);
+
+ return $catalogue;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Config\Util\XmlUtils;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * XliffFileLoader loads translations from XLIFF files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class XliffFileLoader implements LoaderInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($resource, $locale, $domain = 'messages')
+ {
+ if (!stream_is_local($resource)) {
+ throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+ }
+
+ if (!file_exists($resource)) {
+ throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+ }
+
+ $catalogue = new MessageCatalogue($locale);
+ $this->extract($resource, $catalogue, $domain);
+
+ if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
+ $catalogue->addResource(new FileResource($resource));
+ }
+
+ return $catalogue;
+ }
+
+ private function extract($resource, MessageCatalogue $catalogue, $domain)
+ {
+ try {
+ $dom = XmlUtils::loadFile($resource);
+ } catch (\InvalidArgumentException $e) {
+ throw new InvalidResourceException(sprintf('Unable to load "%s": %s', $resource, $e->getMessage()), $e->getCode(), $e);
+ }
+
+ $xliffVersion = $this->getVersionNumber($dom);
+ $this->validateSchema($xliffVersion, $dom, $this->getSchema($xliffVersion));
+
+ if ('1.2' === $xliffVersion) {
+ $this->extractXliff1($dom, $catalogue, $domain);
+ }
+
+ if ('2.0' === $xliffVersion) {
+ $this->extractXliff2($dom, $catalogue, $domain);
+ }
+ }
+
+ /**
+ * Extract messages and metadata from DOMDocument into a MessageCatalogue.
+ *
+ * @param \DOMDocument $dom Source to extract messages and metadata
+ * @param MessageCatalogue $catalogue Catalogue where we'll collect messages and metadata
+ * @param string $domain The domain
+ */
+ private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, $domain)
+ {
+ $xml = simplexml_import_dom($dom);
+ $encoding = strtoupper($dom->encoding);
+
+ $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2');
+ foreach ($xml->xpath('//xliff:trans-unit') as $translation) {
+ $attributes = $translation->attributes();
+
+ if (!(isset($attributes['resname']) || isset($translation->source))) {
+ continue;
+ }
+
+ $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source;
+ // If the xlf file has another encoding specified, try to convert it because
+ // simple_xml will always return utf-8 encoded values
+ $target = $this->utf8ToCharset((string) (isset($translation->target) ? $translation->target : $source), $encoding);
+
+ $catalogue->set((string) $source, $target, $domain);
+
+ $metadata = array();
+ if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) {
+ $metadata['notes'] = $notes;
+ }
+
+ if (isset($translation->target) && $translation->target->attributes()) {
+ $metadata['target-attributes'] = array();
+ foreach ($translation->target->attributes() as $key => $value) {
+ $metadata['target-attributes'][$key] = (string) $value;
+ }
+ }
+
+ if (isset($attributes['id'])) {
+ $metadata['id'] = (string) $attributes['id'];
+ }
+
+ $catalogue->setMetadata((string) $source, $metadata, $domain);
+ }
+ }
+
+ /**
+ * @param \DOMDocument $dom
+ * @param MessageCatalogue $catalogue
+ * @param string $domain
+ */
+ private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, $domain)
+ {
+ $xml = simplexml_import_dom($dom);
+ $encoding = strtoupper($dom->encoding);
+
+ $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0');
+
+ foreach ($xml->xpath('//xliff:unit/xliff:segment') as $segment) {
+ $source = $segment->source;
+
+ // If the xlf file has another encoding specified, try to convert it because
+ // simple_xml will always return utf-8 encoded values
+ $target = $this->utf8ToCharset((string) (isset($segment->target) ? $segment->target : $source), $encoding);
+
+ $catalogue->set((string) $source, $target, $domain);
+
+ $metadata = array();
+ if (isset($segment->target) && $segment->target->attributes()) {
+ $metadata['target-attributes'] = array();
+ foreach ($segment->target->attributes() as $key => $value) {
+ $metadata['target-attributes'][$key] = (string) $value;
+ }
+ }
+
+ $catalogue->setMetadata((string) $source, $metadata, $domain);
+ }
+ }
+
+ /**
+ * Convert a UTF8 string to the specified encoding.
+ *
+ * @param string $content String to decode
+ * @param string $encoding Target encoding
+ *
+ * @return string
+ */
+ private function utf8ToCharset($content, $encoding = null)
+ {
+ if ('UTF-8' !== $encoding && !empty($encoding)) {
+ return mb_convert_encoding($content, $encoding, 'UTF-8');
+ }
+
+ return $content;
+ }
+
+ /**
+ * Validates and parses the given file into a DOMDocument.
+ *
+ * @param string $file
+ * @param \DOMDocument $dom
+ * @param string $schema source of the schema
+ *
+ * @throws InvalidResourceException
+ */
+ private function validateSchema($file, \DOMDocument $dom, $schema)
+ {
+ $internalErrors = libxml_use_internal_errors(true);
+
+ $disableEntities = libxml_disable_entity_loader(false);
+
+ if (!@$dom->schemaValidateSource($schema)) {
+ libxml_disable_entity_loader($disableEntities);
+
+ throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s', $file, implode("\n", $this->getXmlErrors($internalErrors))));
+ }
+
+ libxml_disable_entity_loader($disableEntities);
+
+ $dom->normalizeDocument();
+
+ libxml_clear_errors();
+ libxml_use_internal_errors($internalErrors);
+ }
+
+ private function getSchema($xliffVersion)
+ {
+ if ('1.2' === $xliffVersion) {
+ $schemaSource = file_get_contents(__DIR__.'/schema/dic/xliff-core/xliff-core-1.2-strict.xsd');
+ $xmlUri = 'http://www.w3.org/2001/xml.xsd';
+ } elseif ('2.0' === $xliffVersion) {
+ $schemaSource = file_get_contents(__DIR__.'/schema/dic/xliff-core/xliff-core-2.0.xsd');
+ $xmlUri = 'informativeCopiesOf3rdPartySchemas/w3c/xml.xsd';
+ } else {
+ throw new InvalidArgumentException(sprintf('No support implemented for loading XLIFF version "%s".', $xliffVersion));
+ }
+
+ return $this->fixXmlLocation($schemaSource, $xmlUri);
+ }
+
+ /**
+ * Internally changes the URI of a dependent xsd to be loaded locally.
+ *
+ * @param string $schemaSource Current content of schema file
+ * @param string $xmlUri External URI of XML to convert to local
+ *
+ * @return string
+ */
+ private function fixXmlLocation($schemaSource, $xmlUri)
+ {
+ $newPath = str_replace('\\', '/', __DIR__).'/schema/dic/xliff-core/xml.xsd';
+ $parts = explode('/', $newPath);
+ if (0 === stripos($newPath, 'phar://')) {
+ $tmpfile = tempnam(sys_get_temp_dir(), 'sf2');
+ if ($tmpfile) {
+ copy($newPath, $tmpfile);
+ $parts = explode('/', str_replace('\\', '/', $tmpfile));
+ }
+ }
+
+ $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
+ $newPath = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts));
+
+ return str_replace($xmlUri, $newPath, $schemaSource);
+ }
+
+ /**
+ * Returns the XML errors of the internal XML parser.
+ *
+ * @param bool $internalErrors
+ *
+ * @return array An array of errors
+ */
+ private function getXmlErrors($internalErrors)
+ {
+ $errors = array();
+ foreach (libxml_get_errors() as $error) {
+ $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
+ LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
+ $error->code,
+ trim($error->message),
+ $error->file ?: 'n/a',
+ $error->line,
+ $error->column
+ );
+ }
+
+ libxml_clear_errors();
+ libxml_use_internal_errors($internalErrors);
+
+ return $errors;
+ }
+
+ /**
+ * Gets xliff file version based on the root "version" attribute.
+ * Defaults to 1.2 for backwards compatibility.
+ *
+ * @param \DOMDocument $dom
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return string
+ */
+ private function getVersionNumber(\DOMDocument $dom)
+ {
+ /** @var \DOMNode $xliff */
+ foreach ($dom->getElementsByTagName('xliff') as $xliff) {
+ $version = $xliff->attributes->getNamedItem('version');
+ if ($version) {
+ return $version->nodeValue;
+ }
+
+ $namespace = $xliff->attributes->getNamedItem('xmlns');
+ if ($namespace) {
+ if (0 !== substr_compare('urn:oasis:names:tc:xliff:document:', $namespace->nodeValue, 0, 34)) {
+ throw new InvalidArgumentException(sprintf('Not a valid XLIFF namespace "%s"', $namespace));
+ }
+
+ return substr($namespace, 34);
+ }
+ }
+
+ // Falls back to v1.2
+ return '1.2';
+ }
+
+ /**
+ * @param \SimpleXMLElement|null $noteElement
+ * @param string|null $encoding
+ *
+ * @return array
+ */
+ private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, $encoding = null)
+ {
+ $notes = array();
+
+ if (null === $noteElement) {
+ return $notes;
+ }
+
+ /** @var \SimpleXMLElement $xmlNote */
+ foreach ($noteElement as $xmlNote) {
+ $noteAttributes = $xmlNote->attributes();
+ $note = array('content' => $this->utf8ToCharset((string) $xmlNote, $encoding));
+ if (isset($noteAttributes['priority'])) {
+ $note['priority'] = (int) $noteAttributes['priority'];
+ }
+
+ if (isset($noteAttributes['from'])) {
+ $note['from'] = (string) $noteAttributes['from'];
+ }
+
+ $notes[] = $note;
+ }
+
+ return $notes;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\LogicException;
+use Symfony\Component\Yaml\Parser as YamlParser;
+use Symfony\Component\Yaml\Exception\ParseException;
+use Symfony\Component\Yaml\Yaml;
+
+/**
+ * YamlFileLoader loads translations from Yaml files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class YamlFileLoader extends FileLoader
+{
+ private $yamlParser;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function loadResource($resource)
+ {
+ if (null === $this->yamlParser) {
+ if (!class_exists('Symfony\Component\Yaml\Parser')) {
+ throw new LogicException('Loading translations from the YAML format requires the Symfony Yaml component.');
+ }
+
+ $this->yamlParser = new YamlParser();
+ }
+
+ $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($resource, &$prevErrorHandler) {
+ $message = E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$resource.'"$0', $message) : $message;
+
+ return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false;
+ });
+
+ try {
+ $messages = $this->yamlParser->parse(file_get_contents($resource), Yaml::PARSE_KEYS_AS_STRINGS);
+ } catch (ParseException $e) {
+ throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s"', $resource), 0, $e);
+ } finally {
+ restore_error_handler();
+ }
+
+ return $messages;
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+
+May-19-2004:
+- Changed the <choice> for ElemType_header, moving minOccurs="0" maxOccurs="unbounded" from its elements
+to <choice> itself.
+- Added <choice> for ElemType_trans-unit to allow "any order" for <context-group>, <count-group>, <prop-group>, <note>, and
+<alt-trans>.
+
+Oct-2005
+- updated version info to 1.2
+- equiv-trans attribute to <trans-unit> element
+- merged-trans attribute for <group> element
+- Add the <seg-source> element as optional in the <trans-unit> and <alt-trans> content models, at the same level as <source>
+- Create a new value "seg" for the mtype attribute of the <mrk> element
+- Add mid as an optional attribute for the <alt-trans> element
+
+Nov-14-2005
+- Changed name attribute for <context-group> from required to optional
+- Added extension point at <xliff>
+
+Jan-9-2006
+- Added alttranstype type attribute to <alt-trans>, and values
+
+Jan-10-2006
+- Corrected error with overwritten purposeValueList
+- Corrected name="AttrType_Version", attribute should have been "name"
+
+-->
+<xsd:schema xmlns:xlf="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:oasis:names:tc:xliff:document:1.2" xml:lang="en">
+ <!-- Import for xml:lang and xml:space -->
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+ <!-- Attributes Lists -->
+ <xsd:simpleType name="XTend">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="x-[^\s]+"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="context-typeValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'context-type'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="database">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a database content.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="element">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the content of an element within an XML document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="elementtitle">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the name of an element within an XML document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="linenumber">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the line number from the sourcefile (see context-type="sourcefile") where the <source> is found.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="numparams">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a the number of parameters contained within the <source>.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="paramnotes">
+ <xsd:annotation>
+ <xsd:documentation>Indicates notes pertaining to the parameters in the <source>.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="record">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the content of a record within a database.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="recordtitle">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the name of a record within a database.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="sourcefile">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the original source file in the case that multiple files are merged to form the original file from which the XLIFF file is created. This differs from the original <file> attribute in that this sourcefile is one of many that make up that file.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="count-typeValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'count-type'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="num-usages">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the count units are items that are used X times in a certain context; example: this is a reusable text unit which is used 42 times in other texts.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="repetition">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the count units are translation units existing already in the same document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="total">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a total count.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="InlineDelimitersValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'ctype' when used other elements than <ph> or <x>.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="bold">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a run of bolded text.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="italic">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a run of text in italics.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="underlined">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a run of underlined text.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="link">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a run of hyper-text.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="InlinePlaceholdersValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'ctype' when used with <ph> or <x>.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="image">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a inline image.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pb">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a page break.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="lb">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a line break.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="mime-typeValueList">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="(text|multipart|message|application|image|audio|video|model)(/.+)*"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="datatypeValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'datatype'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="asp">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Active Server Page data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="c">
+ <xsd:annotation>
+ <xsd:documentation>Indicates C source file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cdf">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Channel Definition Format (CDF) data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cfm">
+ <xsd:annotation>
+ <xsd:documentation>Indicates ColdFusion data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cpp">
+ <xsd:annotation>
+ <xsd:documentation>Indicates C++ source file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="csharp">
+ <xsd:annotation>
+ <xsd:documentation>Indicates C-Sharp data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cstring">
+ <xsd:annotation>
+ <xsd:documentation>Indicates strings from C, ASM, and driver files data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="csv">
+ <xsd:annotation>
+ <xsd:documentation>Indicates comma-separated values data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="database">
+ <xsd:annotation>
+ <xsd:documentation>Indicates database data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="documentfooter">
+ <xsd:annotation>
+ <xsd:documentation>Indicates portions of document that follows data and contains metadata.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="documentheader">
+ <xsd:annotation>
+ <xsd:documentation>Indicates portions of document that precedes data and contains metadata.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="filedialog">
+ <xsd:annotation>
+ <xsd:documentation>Indicates data from standard UI file operations dialogs (e.g., Open, Save, Save As, Export, Import).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="form">
+ <xsd:annotation>
+ <xsd:documentation>Indicates standard user input screen data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="html">
+ <xsd:annotation>
+ <xsd:documentation>Indicates HyperText Markup Language (HTML) data - document instance.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="htmlbody">
+ <xsd:annotation>
+ <xsd:documentation>Indicates content within an HTML document’s <body> element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="ini">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Windows INI file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="interleaf">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Interleaf data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="javaclass">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Java source file data (extension '.java').</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="javapropertyresourcebundle">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Java property resource bundle data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="javalistresourcebundle">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Java list resource bundle data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="javascript">
+ <xsd:annotation>
+ <xsd:documentation>Indicates JavaScript source file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="jscript">
+ <xsd:annotation>
+ <xsd:documentation>Indicates JScript source file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="layout">
+ <xsd:annotation>
+ <xsd:documentation>Indicates information relating to formatting.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="lisp">
+ <xsd:annotation>
+ <xsd:documentation>Indicates LISP source file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="margin">
+ <xsd:annotation>
+ <xsd:documentation>Indicates information relating to margin formats.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="menufile">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a file containing menu.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="messagefile">
+ <xsd:annotation>
+ <xsd:documentation>Indicates numerically identified string table.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="mif">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Maker Interchange Format (MIF) data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="mimetype">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the datatype attribute value is a MIME Type value and is defined in the mime-type attribute.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="mo">
+ <xsd:annotation>
+ <xsd:documentation>Indicates GNU Machine Object data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="msglib">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Message Librarian strings created by Novell's Message Librarian Tool.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pagefooter">
+ <xsd:annotation>
+ <xsd:documentation>Indicates information to be displayed at the bottom of each page of a document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pageheader">
+ <xsd:annotation>
+ <xsd:documentation>Indicates information to be displayed at the top of each page of a document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="parameters">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a list of property values (e.g., settings within INI files or preferences dialog).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pascal">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Pascal source file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="php">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Hypertext Preprocessor data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="plaintext">
+ <xsd:annotation>
+ <xsd:documentation>Indicates plain text file (no formatting other than, possibly, wrapping).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="po">
+ <xsd:annotation>
+ <xsd:documentation>Indicates GNU Portable Object file.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="report">
+ <xsd:annotation>
+ <xsd:documentation>Indicates dynamically generated user defined document. e.g. Oracle Report, Crystal Report, etc.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="resources">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Windows .NET binary resources.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="resx">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Windows .NET Resources.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rtf">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Rich Text Format (RTF) data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="sgml">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Standard Generalized Markup Language (SGML) data - document instance.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="sgmldtd">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Standard Generalized Markup Language (SGML) data - Document Type Definition (DTD).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="svg">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Scalable Vector Graphic (SVG) data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="vbscript">
+ <xsd:annotation>
+ <xsd:documentation>Indicates VisualBasic Script source file.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="warning">
+ <xsd:annotation>
+ <xsd:documentation>Indicates warning message.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="winres">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Windows (Win32) resources (i.e. resources extracted from an RC script, a message file, or a compiled file).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="xhtml">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Extensible HyperText Markup Language (XHTML) data - document instance.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="xml">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Extensible Markup Language (XML) data - document instance.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="xmldtd">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Extensible Markup Language (XML) data - Document Type Definition (DTD).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="xsl">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Extensible Stylesheet Language (XSL) data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="xul">
+ <xsd:annotation>
+ <xsd:documentation>Indicates XUL elements.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="mtypeValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'mtype'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="abbrev">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the marked text is an abbreviation.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="abbreviated-form">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.8: A term resulting from the omission of any part of the full term while designating the same concept.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="abbreviation">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.8.1: An abbreviated form of a simple term resulting from the omission of some of its letters (e.g. 'adj.' for 'adjective').</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="acronym">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.8.4: An abbreviated form of a term made up of letters from the full form of a multiword term strung together into a sequence pronounced only syllabically (e.g. 'radar' for 'radio detecting and ranging').</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="appellation">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620: A proper-name term, such as the name of an agency or other proper entity.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="collocation">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.18.1: A recurrent word combination characterized by cohesion in that the components of the collocation must co-occur within an utterance or series of utterances, even though they do not necessarily have to maintain immediate proximity to one another.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="common-name">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.5: A synonym for an international scientific term that is used in general discourse in a given language.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="datetime">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the marked text is a date and/or time.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="equation">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.15: An expression used to represent a concept based on a statement that two mathematical expressions are, for instance, equal as identified by the equal sign (=), or assigned to one another by a similar sign.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="expanded-form">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.7: The complete representation of a term for which there is an abbreviated form.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="formula">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.14: Figures, symbols or the like used to express a concept briefly, such as a mathematical or chemical formula.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="head-term">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.1: The concept designation that has been chosen to head a terminological record.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="initialism">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.8.3: An abbreviated form of a term consisting of some of the initial letters of the words making up a multiword term or the term elements making up a compound term when these letters are pronounced individually (e.g. 'BSE' for 'bovine spongiform encephalopathy').</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="international-scientific-term">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.4: A term that is part of an international scientific nomenclature as adopted by an appropriate scientific body.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="internationalism">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.6: A term that has the same or nearly identical orthographic or phonemic form in many languages.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="logical-expression">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.16: An expression used to represent a concept based on mathematical or logical relations, such as statements of inequality, set relationships, Boolean operations, and the like.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="materials-management-unit">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.17: A unit to track object.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="name">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the marked text is a name.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="near-synonym">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.3: A term that represents the same or a very similar concept as another term in the same language, but for which interchangeability is limited to some contexts and inapplicable in others.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="part-number">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.17.2: A unique alphanumeric designation assigned to an object in a manufacturing system.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="phrase">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the marked text is a phrase.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="phraseological-unit">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.18: Any group of two or more words that form a unit, the meaning of which frequently cannot be deduced based on the combined sense of the words making up the phrase.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="protected">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the marked text should not be translated.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="romanized-form">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.12: A form of a term resulting from an operation whereby non-Latin writing systems are converted to the Latin alphabet.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="seg">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the marked text represents a segment.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="set-phrase">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.18.2: A fixed, lexicalized phrase.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="short-form">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.8.2: A variant of a multiword term that includes fewer words than the full form of the term (e.g. 'Group of Twenty-four' for 'Intergovernmental Group of Twenty-four on International Monetary Affairs').</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="sku">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.17.1: Stock keeping unit, an inventory item identified by a unique alphanumeric designation assigned to an object in an inventory control system.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="standard-text">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.19: A fixed chunk of recurring text.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="symbol">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.13: A designation of a concept by letters, numerals, pictograms or any combination thereof.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="synonym">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.2: Any term that represents the same or a very similar concept as the main entry term in a term entry.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="synonymous-phrase">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.18.3: Phraseological unit in a language that expresses the same semantic content as another phrase in that same language.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="term">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the marked text is a term.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="transcribed-form">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.11: A form of a term resulting from an operation whereby the characters of one writing system are represented by characters from another writing system, taking into account the pronunciation of the characters converted.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="transliterated-form">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.10: A form of a term resulting from an operation whereby the characters of an alphabetic writing system are represented by characters from another alphabetic writing system.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="truncated-term">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.8.5: An abbreviated form of a term resulting from the omission of one or more term elements or syllables (e.g. 'flu' for 'influenza').</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="variant">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.9: One of the alternate forms of a term.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="restypeValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'restype'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="auto3state">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC AUTO3STATE control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="autocheckbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC AUTOCHECKBOX control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="autoradiobutton">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC AUTORADIOBUTTON control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="bedit">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC BEDIT control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="bitmap">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a bitmap, for example a BITMAP resource in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="button">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a button object, for example a BUTTON control Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="caption">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a caption, such as the caption of a dialog box.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cell">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the cell in a table, for example the content of the <td> element in HTML.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="checkbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates check box object, for example a CHECKBOX control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="checkboxmenuitem">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a menu item with an associated checkbox.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="checkedlistbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a list box, but with a check-box for each item.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="colorchooser">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a color selection dialog.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="combobox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a combination of edit box and listbox object, for example a COMBOBOX control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="comboboxexitem">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an initialization entry of an extended combobox DLGINIT resource block. (code 0x1234).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="comboboxitem">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an initialization entry of a combobox DLGINIT resource block (code 0x0403).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="component">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a UI base class element that cannot be represented by any other element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="contextmenu">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a context menu.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="ctext">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC CTEXT control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cursor">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a cursor, for example a CURSOR resource in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="datetimepicker">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a date/time picker.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="defpushbutton">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC DEFPUSHBUTTON control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="dialog">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a dialog box.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="dlginit">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC DLGINIT resource block.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="edit">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an edit box object, for example an EDIT control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="file">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a filename.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="filechooser">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a file dialog.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="fn">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a footnote.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="font">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a font name.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="footer">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a footer.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="frame">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a frame object.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="grid">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a XUL grid element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="groupbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a groupbox object, for example a GROUPBOX control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="header">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a header item.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="heading">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a heading, such has the content of <h1>, <h2>, etc. in HTML.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="hedit">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC HEDIT control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="hscrollbar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a horizontal scrollbar.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="icon">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an icon, for example an ICON resource in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="iedit">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC IEDIT control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="keywords">
+ <xsd:annotation>
+ <xsd:documentation>Indicates keyword list, such as the content of the Keywords meta-data in HTML, or a K footnote in WinHelp RTF.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="label">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a label object.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="linklabel">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a label that is also a HTML link (not necessarily a URL).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="list">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a list (a group of list-items, for example an <ol> or <ul> element in HTML).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="listbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a listbox object, for example an LISTBOX control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="listitem">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an list item (an entry in a list).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="ltext">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC LTEXT control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="menu">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a menu (a group of menu-items).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="menubar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a toolbar containing one or more tope level menus.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="menuitem">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a menu item (an entry in a menu).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="menuseparator">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a XUL menuseparator element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="message">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a message, for example an entry in a MESSAGETABLE resource in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="monthcalendar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a calendar control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="numericupdown">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an edit box beside a spin control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="panel">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a catch all for rectangular areas.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="popupmenu">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a standalone menu not necessarily associated with a menubar.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pushbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a pushbox object, for example a PUSHBOX control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pushbutton">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC PUSHBUTTON control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="radio">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a radio button object.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="radiobuttonmenuitem">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a menuitem with associated radio button.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rcdata">
+ <xsd:annotation>
+ <xsd:documentation>Indicates raw data resources for an application.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="row">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a row in a table.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rtext">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC RTEXT control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="scrollpane">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a user navigable container used to show a portion of a document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="separator">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a generic divider object (e.g. menu group separator).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="shortcut">
+ <xsd:annotation>
+ <xsd:documentation>Windows accelerators, shortcuts in resource or property files.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="spinner">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a UI control to indicate process activity but not progress.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="splitter">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a splitter bar.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="state3">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC STATE3 control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="statusbar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a window for providing feedback to the users, like 'read-only', etc.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="string">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a string, for example an entry in a STRINGTABLE resource in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="tabcontrol">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a layers of controls with a tab to select layers.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="table">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a display and edits regular two-dimensional tables of cells.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="textbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a XUL textbox element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="togglebutton">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a UI button that can be toggled to on or off state.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="toolbar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an array of controls, usually buttons.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="tooltip">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a pop up tool tip text.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="trackbar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a bar with a pointer indicating a position within a certain range.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="tree">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a control that displays a set of hierarchical data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="uri">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a URI (URN or URL).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="userbutton">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC USERBUTTON control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="usercontrol">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a user-defined control like CONTROL control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="var">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the text of a variable.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="versioninfo">
+ <xsd:annotation>
+ <xsd:documentation>Indicates version information about a resource like VERSIONINFO in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="vscrollbar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a vertical scrollbar.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="window">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a graphical window.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="size-unitValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'size-unit'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="byte">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in 8-bit bytes.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="char">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in Unicode characters.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="col">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in columns. Used for HTML text area.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cm">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in centimeters.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="dlgunit">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in dialog units, as defined in Windows resources.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="em">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in 'font-size' units (as defined in CSS).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="ex">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in 'x-height' units (as defined in CSS).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="glyph">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in glyphs. A glyph is considered to be one or more combined Unicode characters that represent a single displayable text character. Sometimes referred to as a 'grapheme cluster'</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="in">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in inches.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="mm">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in millimeters.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="percent">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in percentage.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pixel">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in pixels.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="point">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in point.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="row">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in rows. Used for HTML text area.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="stateValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'state'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="final">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the terminating state.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="needs-adaptation">
+ <xsd:annotation>
+ <xsd:documentation>Indicates only non-textual information needs adaptation.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="needs-l10n">
+ <xsd:annotation>
+ <xsd:documentation>Indicates both text and non-textual information needs adaptation.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="needs-review-adaptation">
+ <xsd:annotation>
+ <xsd:documentation>Indicates only non-textual information needs review.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="needs-review-l10n">
+ <xsd:annotation>
+ <xsd:documentation>Indicates both text and non-textual information needs review.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="needs-review-translation">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that only the text of the item needs to be reviewed.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="needs-translation">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item needs to be translated.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="new">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item is new. For example, translation units that were not in a previous version of the document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="signed-off">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that changes are reviewed and approved.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="translated">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item has been translated.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="state-qualifierValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'state-qualifier'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="exact-match">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an exact match. An exact match occurs when a source text of a segment is exactly the same as the source text of a segment that was translated previously.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="fuzzy-match">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a fuzzy match. A fuzzy match occurs when a source text of a segment is very similar to the source text of a segment that was translated previously (e.g. when the difference is casing, a few changed words, white-space discripancy, etc.).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="id-match">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a match based on matching IDs (in addition to matching text).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="leveraged-glossary">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a translation derived from a glossary.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="leveraged-inherited">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a translation derived from existing translation.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="leveraged-mt">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a translation derived from machine translation.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="leveraged-repository">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a translation derived from a translation repository.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="leveraged-tm">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a translation derived from a translation memory.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="mt-suggestion">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the translation is suggested by machine translation.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rejected-grammar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item has been rejected because of incorrect grammar.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rejected-inaccurate">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item has been rejected because it is incorrect.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rejected-length">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item has been rejected because it is too long or too short.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rejected-spelling">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item has been rejected because of incorrect spelling.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="tm-suggestion">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the translation is suggested by translation memory.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="unitValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'unit'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="word">
+ <xsd:annotation>
+ <xsd:documentation>Refers to words.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="page">
+ <xsd:annotation>
+ <xsd:documentation>Refers to pages.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="trans-unit">
+ <xsd:annotation>
+ <xsd:documentation>Refers to <trans-unit> elements.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="bin-unit">
+ <xsd:annotation>
+ <xsd:documentation>Refers to <bin-unit> elements.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="glyph">
+ <xsd:annotation>
+ <xsd:documentation>Refers to glyphs.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="item">
+ <xsd:annotation>
+ <xsd:documentation>Refers to <trans-unit> and/or <bin-unit> elements.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="instance">
+ <xsd:annotation>
+ <xsd:documentation>Refers to the occurrences of instances defined by the count-type value.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="character">
+ <xsd:annotation>
+ <xsd:documentation>Refers to characters.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="line">
+ <xsd:annotation>
+ <xsd:documentation>Refers to lines.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="sentence">
+ <xsd:annotation>
+ <xsd:documentation>Refers to sentences.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="paragraph">
+ <xsd:annotation>
+ <xsd:documentation>Refers to paragraphs.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="segment">
+ <xsd:annotation>
+ <xsd:documentation>Refers to segments.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="placeable">
+ <xsd:annotation>
+ <xsd:documentation>Refers to placeables (inline elements).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="priorityValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'priority'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:positiveInteger">
+ <xsd:enumeration value="1">
+ <xsd:annotation>
+ <xsd:documentation>Highest priority.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="2">
+ <xsd:annotation>
+ <xsd:documentation>High priority.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="3">
+ <xsd:annotation>
+ <xsd:documentation>High priority, but not as important as 2.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="4">
+ <xsd:annotation>
+ <xsd:documentation>High priority, but not as important as 3.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="5">
+ <xsd:annotation>
+ <xsd:documentation>Medium priority, but more important than 6.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="6">
+ <xsd:annotation>
+ <xsd:documentation>Medium priority, but less important than 5.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="7">
+ <xsd:annotation>
+ <xsd:documentation>Low priority, but more important than 8.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="8">
+ <xsd:annotation>
+ <xsd:documentation>Low priority, but more important than 9.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="9">
+ <xsd:annotation>
+ <xsd:documentation>Low priority.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="10">
+ <xsd:annotation>
+ <xsd:documentation>Lowest priority.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="reformatValueYesNo">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="yes">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that all properties can be reformatted. This value must be used alone.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="no">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that no properties should be reformatted. This value must be used alone.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="reformatValueList">
+ <xsd:list>
+ <xsd:simpleType>
+ <xsd:union memberTypes="xlf:XTend">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="coord">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that all information in the coord attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="coord-x">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the x information in the coord attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="coord-y">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the y information in the coord attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="coord-cx">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the cx information in the coord attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="coord-cy">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the cy information in the coord attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="font">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that all the information in the font attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="font-name">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the name information in the font attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="font-size">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the size information in the font attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="font-weight">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the weight information in the font attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="css-style">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the information in the css-style attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="style">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the information in the style attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="ex-style">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the information in the exstyle attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:union>
+ </xsd:simpleType>
+ </xsd:list>
+ </xsd:simpleType>
+ <xsd:simpleType name="purposeValueList">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="information">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the context is informational in nature, specifying for example, how a term should be translated. Thus, should be displayed to anyone editing the XLIFF document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="location">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the context-group is used to specify where the term was found in the translatable source. Thus, it is not displayed.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="match">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the context information should be used during translation memory lookups. Thus, it is not displayed.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="alttranstypeValueList">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="proposal">
+ <xsd:annotation>
+ <xsd:documentation>Represents a translation proposal from a translation memory or other resource.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="previous-version">
+ <xsd:annotation>
+ <xsd:documentation>Represents a previous version of the target element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rejected">
+ <xsd:annotation>
+ <xsd:documentation>Represents a rejected version of the target element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="reference">
+ <xsd:annotation>
+ <xsd:documentation>Represents a translation to be used for reference purposes only, for example from a related product or a different language.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="accepted">
+ <xsd:annotation>
+ <xsd:documentation>Represents a proposed translation that was used for the translation of the trans-unit, possibly modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <!-- Other Types -->
+ <xsd:complexType name="ElemType_ExternalReference">
+ <xsd:choice>
+ <xsd:element ref="xlf:internal-file"/>
+ <xsd:element ref="xlf:external-file"/>
+ </xsd:choice>
+ </xsd:complexType>
+ <xsd:simpleType name="AttrType_purpose">
+ <xsd:list>
+ <xsd:simpleType>
+ <xsd:union memberTypes="xlf:purposeValueList xlf:XTend"/>
+ </xsd:simpleType>
+ </xsd:list>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_datatype">
+ <xsd:union memberTypes="xlf:datatypeValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_restype">
+ <xsd:union memberTypes="xlf:restypeValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_alttranstype">
+ <xsd:union memberTypes="xlf:alttranstypeValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_context-type">
+ <xsd:union memberTypes="xlf:context-typeValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_state">
+ <xsd:union memberTypes="xlf:stateValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_state-qualifier">
+ <xsd:union memberTypes="xlf:state-qualifierValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_count-type">
+ <xsd:union memberTypes="xlf:restypeValueList xlf:count-typeValueList xlf:datatypeValueList xlf:stateValueList xlf:state-qualifierValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_InlineDelimiters">
+ <xsd:union memberTypes="xlf:InlineDelimitersValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_InlinePlaceholders">
+ <xsd:union memberTypes="xlf:InlinePlaceholdersValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_size-unit">
+ <xsd:union memberTypes="xlf:size-unitValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_mtype">
+ <xsd:union memberTypes="xlf:mtypeValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_unit">
+ <xsd:union memberTypes="xlf:unitValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_priority">
+ <xsd:union memberTypes="xlf:priorityValueList"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_reformat">
+ <xsd:union memberTypes="xlf:reformatValueYesNo xlf:reformatValueList"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_YesNo">
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="yes"/>
+ <xsd:enumeration value="no"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_Position">
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="open"/>
+ <xsd:enumeration value="close"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_assoc">
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="preceding"/>
+ <xsd:enumeration value="following"/>
+ <xsd:enumeration value="both"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_annotates">
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="source"/>
+ <xsd:enumeration value="target"/>
+ <xsd:enumeration value="general"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_Coordinates">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'coord'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="(-?\d+|#);(-?\d+|#);(-?\d+|#);(-?\d+|#)"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_Version">
+ <xsd:annotation>
+ <xsd:documentation>Version values: 1.0 and 1.1 are allowed for backward compatibility.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="1.2"/>
+ <xsd:enumeration value="1.1"/>
+ <xsd:enumeration value="1.0"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <!-- Groups -->
+ <xsd:group name="ElemGroup_TextContent">
+ <xsd:choice>
+ <xsd:element ref="xlf:g"/>
+ <xsd:element ref="xlf:bpt"/>
+ <xsd:element ref="xlf:ept"/>
+ <xsd:element ref="xlf:ph"/>
+ <xsd:element ref="xlf:it"/>
+ <xsd:element ref="xlf:mrk"/>
+ <xsd:element ref="xlf:x"/>
+ <xsd:element ref="xlf:bx"/>
+ <xsd:element ref="xlf:ex"/>
+ </xsd:choice>
+ </xsd:group>
+ <xsd:attributeGroup name="AttrGroup_TextContent">
+ <xsd:attribute name="id" type="xsd:string" use="required"/>
+ <xsd:attribute name="xid" type="xsd:string" use="optional"/>
+ <xsd:attribute name="equiv-text" type="xsd:string" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:attributeGroup>
+ <!-- XLIFF Structure -->
+ <xsd:element name="xliff">
+ <xsd:complexType>
+ <xsd:sequence maxOccurs="unbounded">
+ <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+ <xsd:element ref="xlf:file"/>
+ </xsd:sequence>
+ <xsd:attribute name="version" type="xlf:AttrType_Version" use="required"/>
+ <xsd:attribute ref="xml:lang" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="file">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element minOccurs="0" ref="xlf:header"/>
+ <xsd:element ref="xlf:body"/>
+ </xsd:sequence>
+ <xsd:attribute name="original" type="xsd:string" use="required"/>
+ <xsd:attribute name="source-language" type="xsd:language" use="required"/>
+ <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="required"/>
+ <xsd:attribute name="tool-id" type="xsd:string" use="optional"/>
+ <xsd:attribute name="date" type="xsd:dateTime" use="optional"/>
+ <xsd:attribute ref="xml:space" use="optional"/>
+ <xsd:attribute name="category" type="xsd:string" use="optional"/>
+ <xsd:attribute name="target-language" type="xsd:language" use="optional"/>
+ <xsd:attribute name="product-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="product-version" type="xsd:string" use="optional"/>
+ <xsd:attribute name="build-num" type="xsd:string" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ <xsd:unique name="U_group_id">
+ <xsd:selector xpath=".//xlf:group"/>
+ <xsd:field xpath="@id"/>
+ </xsd:unique>
+ <xsd:key name="K_unit_id">
+ <xsd:selector xpath=".//xlf:trans-unit|.//xlf:bin-unit"/>
+ <xsd:field xpath="@id"/>
+ </xsd:key>
+ <xsd:keyref name="KR_unit_id" refer="xlf:K_unit_id">
+ <xsd:selector xpath=".//bpt|.//ept|.//it|.//ph|.//g|.//x|.//bx|.//ex|.//sub"/>
+ <xsd:field xpath="@xid"/>
+ </xsd:keyref>
+ <xsd:key name="K_tool-id">
+ <xsd:selector xpath="xlf:header/xlf:tool"/>
+ <xsd:field xpath="@tool-id"/>
+ </xsd:key>
+ <xsd:keyref name="KR_file_tool-id" refer="xlf:K_tool-id">
+ <xsd:selector xpath="."/>
+ <xsd:field xpath="@tool-id"/>
+ </xsd:keyref>
+ <xsd:keyref name="KR_phase_tool-id" refer="xlf:K_tool-id">
+ <xsd:selector xpath="xlf:header/xlf:phase-group/xlf:phase"/>
+ <xsd:field xpath="@tool-id"/>
+ </xsd:keyref>
+ <xsd:keyref name="KR_alt-trans_tool-id" refer="xlf:K_tool-id">
+ <xsd:selector xpath=".//xlf:trans-unit/xlf:alt-trans"/>
+ <xsd:field xpath="@tool-id"/>
+ </xsd:keyref>
+ <xsd:key name="K_count-group_name">
+ <xsd:selector xpath=".//xlf:count-group"/>
+ <xsd:field xpath="@name"/>
+ </xsd:key>
+ <xsd:unique name="U_context-group_name">
+ <xsd:selector xpath=".//xlf:context-group"/>
+ <xsd:field xpath="@name"/>
+ </xsd:unique>
+ <xsd:key name="K_phase-name">
+ <xsd:selector xpath="xlf:header/xlf:phase-group/xlf:phase"/>
+ <xsd:field xpath="@phase-name"/>
+ </xsd:key>
+ <xsd:keyref name="KR_phase-name" refer="xlf:K_phase-name">
+ <xsd:selector xpath=".//xlf:count|.//xlf:trans-unit|.//xlf:target|.//bin-unit|.//bin-target"/>
+ <xsd:field xpath="@phase-name"/>
+ </xsd:keyref>
+ <xsd:unique name="U_uid">
+ <xsd:selector xpath=".//xlf:external-file"/>
+ <xsd:field xpath="@uid"/>
+ </xsd:unique>
+ </xsd:element>
+ <xsd:element name="header">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element minOccurs="0" name="skl" type="xlf:ElemType_ExternalReference"/>
+ <xsd:element minOccurs="0" ref="xlf:phase-group"/>
+ <xsd:choice maxOccurs="unbounded" minOccurs="0">
+ <xsd:element name="glossary" type="xlf:ElemType_ExternalReference"/>
+ <xsd:element name="reference" type="xlf:ElemType_ExternalReference"/>
+ <xsd:element ref="xlf:count-group"/>
+ <xsd:element ref="xlf:note"/>
+ <xsd:element ref="xlf:tool"/>
+ </xsd:choice>
+ <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="internal-file">
+ <xsd:complexType>
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="form" type="xsd:string"/>
+ <xsd:attribute name="crc" type="xsd:NMTOKEN"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="external-file">
+ <xsd:complexType>
+ <xsd:attribute name="href" type="xsd:string" use="required"/>
+ <xsd:attribute name="crc" type="xsd:NMTOKEN"/>
+ <xsd:attribute name="uid" type="xsd:NMTOKEN"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="note">
+ <xsd:complexType>
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute ref="xml:lang" use="optional"/>
+ <xsd:attribute default="1" name="priority" type="xlf:AttrType_priority" use="optional"/>
+ <xsd:attribute name="from" type="xsd:string" use="optional"/>
+ <xsd:attribute default="general" name="annotates" type="xlf:AttrType_annotates" use="optional"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="phase-group">
+ <xsd:complexType>
+ <xsd:sequence maxOccurs="unbounded">
+ <xsd:element ref="xlf:phase"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="phase">
+ <xsd:complexType>
+ <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:note"/>
+ </xsd:sequence>
+ <xsd:attribute name="phase-name" type="xsd:string" use="required"/>
+ <xsd:attribute name="process-name" type="xsd:string" use="required"/>
+ <xsd:attribute name="company-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="tool-id" type="xsd:string" use="optional"/>
+ <xsd:attribute name="date" type="xsd:dateTime" use="optional"/>
+ <xsd:attribute name="job-id" type="xsd:string" use="optional"/>
+ <xsd:attribute name="contact-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="contact-email" type="xsd:string" use="optional"/>
+ <xsd:attribute name="contact-phone" type="xsd:string" use="optional"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="count-group">
+ <xsd:complexType>
+ <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:count"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="count">
+ <xsd:complexType>
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="count-type" type="xlf:AttrType_count-type" use="optional"/>
+ <xsd:attribute name="phase-name" type="xsd:string" use="optional"/>
+ <xsd:attribute default="word" name="unit" type="xlf:AttrType_unit" use="optional"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="context-group">
+ <xsd:complexType>
+ <xsd:sequence maxOccurs="unbounded">
+ <xsd:element ref="xlf:context"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="purpose" type="xlf:AttrType_purpose" use="optional"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="context">
+ <xsd:complexType>
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="context-type" type="xlf:AttrType_context-type" use="required"/>
+ <xsd:attribute default="no" name="match-mandatory" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="tool">
+ <xsd:complexType mixed="true">
+ <xsd:sequence>
+ <xsd:any namespace="##any" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="tool-id" type="xsd:string" use="required"/>
+ <xsd:attribute name="tool-name" type="xsd:string" use="required"/>
+ <xsd:attribute name="tool-version" type="xsd:string" use="optional"/>
+ <xsd:attribute name="tool-company" type="xsd:string" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="body">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded" minOccurs="0">
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:group"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:trans-unit"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:bin-unit"/>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="group">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:sequence>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:context-group"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:count-group"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:note"/>
+ <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+ </xsd:sequence>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:group"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:trans-unit"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:bin-unit"/>
+ </xsd:choice>
+ </xsd:sequence>
+ <xsd:attribute name="id" type="xsd:string" use="optional"/>
+ <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+ <xsd:attribute default="default" ref="xml:space" use="optional"/>
+ <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+ <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+ <xsd:attribute name="extradata" type="xsd:string" use="optional"/>
+ <xsd:attribute name="extype" type="xsd:string" use="optional"/>
+ <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="menu" type="xsd:string" use="optional"/>
+ <xsd:attribute name="menu-option" type="xsd:string" use="optional"/>
+ <xsd:attribute name="menu-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+ <xsd:attribute name="font" type="xsd:string" use="optional"/>
+ <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+ <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/>
+ <xsd:attribute default="pixel" name="size-unit" type="xlf:AttrType_size-unit" use="optional"/>
+ <xsd:attribute name="maxwidth" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="minwidth" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="maxheight" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="minheight" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="maxbytes" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="minbytes" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="charclass" type="xsd:string" use="optional"/>
+ <xsd:attribute default="no" name="merged-trans" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="trans-unit">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element ref="xlf:source"/>
+ <xsd:element minOccurs="0" ref="xlf:seg-source"/>
+ <xsd:element minOccurs="0" ref="xlf:target"/>
+ <xsd:choice maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:context-group"/>
+ <xsd:element ref="xlf:count-group"/>
+ <xsd:element ref="xlf:note"/>
+ <xsd:element ref="xlf:alt-trans"/>
+ </xsd:choice>
+ <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+ </xsd:sequence>
+ <xsd:attribute name="id" type="xsd:string" use="required"/>
+ <xsd:attribute name="approved" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/>
+ <xsd:attribute default="default" ref="xml:space" use="optional"/>
+ <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+ <xsd:attribute name="phase-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+ <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+ <xsd:attribute name="extradata" type="xsd:string" use="optional"/>
+ <xsd:attribute name="extype" type="xsd:string" use="optional"/>
+ <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="menu" type="xsd:string" use="optional"/>
+ <xsd:attribute name="menu-option" type="xsd:string" use="optional"/>
+ <xsd:attribute name="menu-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+ <xsd:attribute name="font" type="xsd:string" use="optional"/>
+ <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+ <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute default="pixel" name="size-unit" type="xlf:AttrType_size-unit" use="optional"/>
+ <xsd:attribute name="maxwidth" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="minwidth" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="maxheight" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="minheight" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="maxbytes" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="minbytes" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="charclass" type="xsd:string" use="optional"/>
+ <xsd:attribute default="yes" name="merged-trans" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ <xsd:unique name="U_tu_segsrc_mid">
+ <xsd:selector xpath="./xlf:seg-source/xlf:mrk"/>
+ <xsd:field xpath="@mid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_tu_segsrc_mid" refer="xlf:U_tu_segsrc_mid">
+ <xsd:selector xpath="./xlf:target/xlf:mrk|./xlf:alt-trans"/>
+ <xsd:field xpath="@mid"/>
+ </xsd:keyref>
+ </xsd:element>
+ <xsd:element name="source">
+ <xsd:complexType mixed="true">
+ <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+ <xsd:attribute ref="xml:lang" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ <xsd:unique name="U_source_bpt_rid">
+ <xsd:selector xpath=".//xlf:bpt"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_source_ept_rid" refer="xlf:U_source_bpt_rid">
+ <xsd:selector xpath=".//xlf:ept"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:keyref>
+ <xsd:unique name="U_source_bx_rid">
+ <xsd:selector xpath=".//xlf:bx"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_source_ex_rid" refer="xlf:U_source_bx_rid">
+ <xsd:selector xpath=".//xlf:ex"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:keyref>
+ </xsd:element>
+ <xsd:element name="seg-source">
+ <xsd:complexType mixed="true">
+ <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+ <xsd:attribute ref="xml:lang" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ <xsd:unique name="U_segsrc_bpt_rid">
+ <xsd:selector xpath=".//xlf:bpt"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_segsrc_ept_rid" refer="xlf:U_segsrc_bpt_rid">
+ <xsd:selector xpath=".//xlf:ept"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:keyref>
+ <xsd:unique name="U_segsrc_bx_rid">
+ <xsd:selector xpath=".//xlf:bx"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_segsrc_ex_rid" refer="xlf:U_segsrc_bx_rid">
+ <xsd:selector xpath=".//xlf:ex"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:keyref>
+ </xsd:element>
+ <xsd:element name="target">
+ <xsd:complexType mixed="true">
+ <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+ <xsd:attribute name="state" type="xlf:AttrType_state" use="optional"/>
+ <xsd:attribute name="state-qualifier" type="xlf:AttrType_state-qualifier" use="optional"/>
+ <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute ref="xml:lang" use="optional"/>
+ <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+ <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+ <xsd:attribute name="font" type="xsd:string" use="optional"/>
+ <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+ <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute default="yes" name="equiv-trans" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ <xsd:unique name="U_target_bpt_rid">
+ <xsd:selector xpath=".//xlf:bpt"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_target_ept_rid" refer="xlf:U_target_bpt_rid">
+ <xsd:selector xpath=".//xlf:ept"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:keyref>
+ <xsd:unique name="U_target_bx_rid">
+ <xsd:selector xpath=".//xlf:bx"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_target_ex_rid" refer="xlf:U_target_bx_rid">
+ <xsd:selector xpath=".//xlf:ex"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:keyref>
+ </xsd:element>
+ <xsd:element name="alt-trans">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element minOccurs="0" ref="xlf:source"/>
+ <xsd:element minOccurs="0" ref="xlf:seg-source"/>
+ <xsd:element maxOccurs="1" ref="xlf:target"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:context-group"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:note"/>
+ <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+ </xsd:sequence>
+ <xsd:attribute name="match-quality" type="xsd:string" use="optional"/>
+ <xsd:attribute name="tool-id" type="xsd:string" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute ref="xml:lang" use="optional"/>
+ <xsd:attribute name="origin" type="xsd:string" use="optional"/>
+ <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+ <xsd:attribute default="default" ref="xml:space" use="optional"/>
+ <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+ <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+ <xsd:attribute name="extradata" type="xsd:string" use="optional"/>
+ <xsd:attribute name="extype" type="xsd:string" use="optional"/>
+ <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="menu" type="xsd:string" use="optional"/>
+ <xsd:attribute name="menu-option" type="xsd:string" use="optional"/>
+ <xsd:attribute name="menu-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="mid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+ <xsd:attribute name="font" type="xsd:string" use="optional"/>
+ <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+ <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute default="proposal" name="alttranstype" type="xlf:AttrType_alttranstype" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ <xsd:unique name="U_at_segsrc_mid">
+ <xsd:selector xpath="./xlf:seg-source/xlf:mrk"/>
+ <xsd:field xpath="@mid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_at_segsrc_mid" refer="xlf:U_at_segsrc_mid">
+ <xsd:selector xpath="./xlf:target/xlf:mrk"/>
+ <xsd:field xpath="@mid"/>
+ </xsd:keyref>
+ </xsd:element>
+ <xsd:element name="bin-unit">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element ref="xlf:bin-source"/>
+ <xsd:element minOccurs="0" ref="xlf:bin-target"/>
+ <xsd:choice maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:context-group"/>
+ <xsd:element ref="xlf:count-group"/>
+ <xsd:element ref="xlf:note"/>
+ <xsd:element ref="xlf:trans-unit"/>
+ </xsd:choice>
+ <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+ </xsd:sequence>
+ <xsd:attribute name="id" type="xsd:string" use="required"/>
+ <xsd:attribute name="mime-type" type="xlf:mime-typeValueList" use="required"/>
+ <xsd:attribute name="approved" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/>
+ <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+ <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+ <xsd:attribute name="phase-name" type="xsd:string" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="bin-source">
+ <xsd:complexType>
+ <xsd:choice>
+ <xsd:element ref="xlf:internal-file"/>
+ <xsd:element ref="xlf:external-file"/>
+ </xsd:choice>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="bin-target">
+ <xsd:complexType>
+ <xsd:choice>
+ <xsd:element ref="xlf:internal-file"/>
+ <xsd:element ref="xlf:external-file"/>
+ </xsd:choice>
+ <xsd:attribute name="mime-type" type="xlf:mime-typeValueList" use="optional"/>
+ <xsd:attribute name="state" type="xlf:AttrType_state" use="optional"/>
+ <xsd:attribute name="state-qualifier" type="xlf:AttrType_state-qualifier" use="optional"/>
+ <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+ <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+ <!-- Element for inline codes -->
+ <xsd:element name="g">
+ <xsd:complexType mixed="true">
+ <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+ <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="x">
+ <xsd:complexType>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlinePlaceholders" use="optional"/>
+ <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="bx">
+ <xsd:complexType>
+ <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+ <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="ex">
+ <xsd:complexType>
+ <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="ph">
+ <xsd:complexType mixed="true">
+ <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:sub"/>
+ </xsd:sequence>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlinePlaceholders" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+ <xsd:attribute name="assoc" type="xlf:AttrType_assoc" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="bpt">
+ <xsd:complexType mixed="true">
+ <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:sub"/>
+ </xsd:sequence>
+ <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="ept">
+ <xsd:complexType mixed="true">
+ <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:sub"/>
+ </xsd:sequence>
+ <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="it">
+ <xsd:complexType mixed="true">
+ <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:sub"/>
+ </xsd:sequence>
+ <xsd:attribute name="pos" type="xlf:AttrType_Position" use="required"/>
+ <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="sub">
+ <xsd:complexType mixed="true">
+ <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+ <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+ <xsd:attribute name="xid" type="xsd:string" use="optional"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="mrk">
+ <xsd:complexType mixed="true">
+ <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+ <xsd:attribute name="mtype" type="xlf:AttrType_mtype" use="required"/>
+ <xsd:attribute name="mid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="comment" type="xsd:string" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+</xsd:schema>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ XLIFF Version 2.0
+ OASIS Standard
+ 05 August 2014
+ Copyright (c) OASIS Open 2014. All rights reserved.
+ Source: http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/schemas/
+ -->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ elementFormDefault="qualified"
+ xmlns:xlf="urn:oasis:names:tc:xliff:document:2.0"
+ targetNamespace="urn:oasis:names:tc:xliff:document:2.0">
+
+ <!-- Import -->
+
+ <xs:import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="informativeCopiesOf3rdPartySchemas/w3c/xml.xsd"/>
+
+ <!-- Element Group -->
+
+ <xs:group name="inline">
+ <xs:choice>
+ <xs:element ref="xlf:cp"/>
+ <xs:element ref="xlf:ph"/>
+ <xs:element ref="xlf:pc"/>
+ <xs:element ref="xlf:sc"/>
+ <xs:element ref="xlf:ec"/>
+ <xs:element ref="xlf:mrk"/>
+ <xs:element ref="xlf:sm"/>
+ <xs:element ref="xlf:em"/>
+ </xs:choice>
+ </xs:group>
+
+ <!-- Attribute Types -->
+
+ <xs:simpleType name="yesNo">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="yes"/>
+ <xs:enumeration value="no"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="yesNoFirstNo">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="yes"/>
+ <xs:enumeration value="firstNo"/>
+ <xs:enumeration value="no"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="dirValue">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="ltr"/>
+ <xs:enumeration value="rtl"/>
+ <xs:enumeration value="auto"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="appliesTo">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="source"/>
+ <xs:enumeration value="target"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="userDefinedValue">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="[^\s:]+:[^\s:]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="attrType_type">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="fmt"/>
+ <xs:enumeration value="ui"/>
+ <xs:enumeration value="quote"/>
+ <xs:enumeration value="link"/>
+ <xs:enumeration value="image"/>
+ <xs:enumeration value="other"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="typeForMrkValues">
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="generic"/>
+ <xs:enumeration value="comment"/>
+ <xs:enumeration value="term"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="attrType_typeForMrk">
+ <xs:union memberTypes="xlf:typeForMrkValues xlf:userDefinedValue"/>
+ </xs:simpleType>
+
+ <xs:simpleType name="priorityValue">
+ <xs:restriction base="xs:positiveInteger">
+ <xs:minInclusive value="1"/>
+ <xs:maxInclusive value="10"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="stateType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="initial"/>
+ <xs:enumeration value="translated"/>
+ <xs:enumeration value="reviewed"/>
+ <xs:enumeration value="final"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- Structural Elements -->
+
+ <xs:element name="xliff">
+ <xs:complexType mixed="false">
+ <xs:sequence>
+ <xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:file"/>
+ </xs:sequence>
+ <xs:attribute name="version" use="required"/>
+ <xs:attribute name="srcLang" use="required"/>
+ <xs:attribute name="trgLang" use="optional"/>
+ <xs:attribute ref="xml:space" use="optional" default="default"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="file">
+ <xs:complexType mixed="false">
+ <xs:sequence>
+ <xs:element minOccurs="0" maxOccurs="1" ref="xlf:skeleton"/>
+ <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
+ processContents="lax"/>
+ <xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/>
+ <xs:choice minOccurs="1" maxOccurs="unbounded">
+ <xs:element ref="xlf:unit"/>
+ <xs:element ref="xlf:group"/>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+ <xs:attribute name="canResegment" use="optional" type="xlf:yesNo" default="yes"/>
+ <xs:attribute name="original" use="optional"/>
+ <xs:attribute name="translate" use="optional" type="xlf:yesNo" default="yes"/>
+ <xs:attribute name="srcDir" use="optional" type="xlf:dirValue" default="auto"/>
+ <xs:attribute name="trgDir" use="optional" type="xlf:dirValue" default="auto"/>
+ <xs:attribute ref="xml:space" use="optional"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="skeleton">
+ <xs:complexType mixed="true">
+ <xs:sequence>
+ <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
+ processContents="lax"/>
+ </xs:sequence>
+ <xs:attribute name="href" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="group">
+ <xs:complexType mixed="false">
+ <xs:sequence>
+ <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
+ processContents="lax"/>
+ <xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="xlf:unit"/>
+ <xs:element ref="xlf:group"/>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+ <xs:attribute name="name" use="optional"/>
+ <xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/>
+ <xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
+ <xs:attribute name="srcDir" use="optional" type="xlf:dirValue"/>
+ <xs:attribute name="trgDir" use="optional" type="xlf:dirValue"/>
+ <xs:attribute name="type" use="optional" type="xlf:userDefinedValue"/>
+ <xs:attribute ref="xml:space" use="optional"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="unit">
+ <xs:complexType mixed="false">
+ <xs:sequence>
+ <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
+ processContents="lax"/>
+ <xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/>
+ <xs:element minOccurs="0" maxOccurs="1" ref="xlf:originalData"/>
+ <xs:choice minOccurs="1" maxOccurs="unbounded">
+ <xs:element ref="xlf:segment"/>
+ <xs:element ref="xlf:ignorable"/>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+ <xs:attribute name="name" use="optional"/>
+ <xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/>
+ <xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
+ <xs:attribute name="srcDir" use="optional" type="xlf:dirValue"/>
+ <xs:attribute name="trgDir" use="optional" type="xlf:dirValue"/>
+ <xs:attribute ref="xml:space" use="optional"/>
+ <xs:attribute name="type" use="optional" type="xlf:userDefinedValue"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="segment">
+ <xs:complexType mixed="false">
+ <xs:sequence>
+ <xs:element minOccurs="1" maxOccurs="1" ref="xlf:source"/>
+ <xs:element minOccurs="0" maxOccurs="1" ref="xlf:target"/>
+ </xs:sequence>
+ <xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/>
+ <xs:attribute name="state" use="optional" type="xlf:stateType" default="initial"/>
+ <xs:attribute name="subState" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="ignorable">
+ <xs:complexType mixed="false">
+ <xs:sequence>
+ <xs:element minOccurs="1" maxOccurs="1" ref="xlf:source"/>
+ <xs:element minOccurs="0" maxOccurs="1" ref="xlf:target"/>
+ </xs:sequence>
+ <xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="notes">
+ <xs:complexType mixed="false">
+ <xs:sequence>
+ <xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:note"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="note">
+ <xs:complexType mixed="true">
+ <xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="appliesTo" use="optional" type="xlf:appliesTo"/>
+ <xs:attribute name="category" use="optional"/>
+ <xs:attribute name="priority" use="optional" type="xlf:priorityValue" default="1"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="originalData">
+ <xs:complexType mixed="false">
+ <xs:sequence>
+ <xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:data"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="data">
+ <xs:complexType mixed="true">
+ <xs:sequence>
+ <xs:element minOccurs="0" maxOccurs="unbounded" ref="xlf:cp"/>
+ </xs:sequence>
+ <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+ <xs:attribute name="dir" use="optional" type="xlf:dirValue" default="auto"/>
+ <xs:attribute ref="xml:space" use="optional" fixed="preserve"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="source">
+ <xs:complexType mixed="true">
+ <xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:attribute ref="xml:lang" use="optional"/>
+ <xs:attribute ref="xml:space" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="target">
+ <xs:complexType mixed="true">
+ <xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:attribute ref="xml:lang" use="optional"/>
+ <xs:attribute ref="xml:space" use="optional"/>
+ <xs:attribute name="order" use="optional" type="xs:positiveInteger"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- Inline Elements -->
+
+ <xs:element name="cp">
+ <!-- Code Point -->
+ <xs:complexType mixed="false">
+ <xs:attribute name="hex" use="required" type="xs:hexBinary"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="ph">
+ <!-- Placeholder -->
+ <xs:complexType mixed="false">
+ <xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
+ <xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
+ <xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
+ <xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="disp" use="optional"/>
+ <xs:attribute name="equiv" use="optional"/>
+ <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+ <xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/>
+ <xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
+ <xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="pc">
+ <!-- Paired Code -->
+ <xs:complexType mixed="true">
+ <xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
+ <xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
+ <xs:attribute name="canOverlap" use="optional" type="xlf:yesNo"/>
+ <xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
+ <xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="dispEnd" use="optional"/>
+ <xs:attribute name="dispStart" use="optional"/>
+ <xs:attribute name="equivEnd" use="optional"/>
+ <xs:attribute name="equivStart" use="optional"/>
+ <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+ <xs:attribute name="dataRefEnd" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="dataRefStart" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="subFlowsEnd" use="optional" type="xs:NMTOKENS"/>
+ <xs:attribute name="subFlowsStart" use="optional" type="xs:NMTOKENS"/>
+ <xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
+ <xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
+ <xs:attribute name="dir" use="optional" type="xlf:dirValue"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="sc">
+ <!-- Start Code -->
+ <xs:complexType mixed="false">
+ <xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
+ <xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
+ <xs:attribute name="canOverlap" use="optional" type="xlf:yesNo" default="yes"/>
+ <xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
+ <xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="dir" use="optional" type="xlf:dirValue"/>
+ <xs:attribute name="disp" use="optional"/>
+ <xs:attribute name="equiv" use="optional"/>
+ <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+ <xs:attribute name="isolated" use="optional" type="xlf:yesNo" default="no"/>
+ <xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/>
+ <xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
+ <xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="ec">
+ <!-- End Code -->
+ <xs:complexType mixed="false">
+ <xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
+ <xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
+ <xs:attribute name="canOverlap" use="optional" type="xlf:yesNo" default="yes"/>
+ <xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
+ <xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="dir" use="optional" type="xlf:dirValue"/>
+ <xs:attribute name="disp" use="optional"/>
+ <xs:attribute name="equiv" use="optional"/>
+ <xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="isolated" use="optional" type="xlf:yesNo" default="no"/>
+ <xs:attribute name="startRef" use="optional" type="xs:NMTOKEN"/>
+ <xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/>
+ <xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
+ <xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="mrk">
+ <!-- Annotation Marker -->
+ <xs:complexType mixed="true">
+ <xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+ <xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
+ <xs:attribute name="type" use="optional" type="xlf:attrType_typeForMrk"/>
+ <xs:attribute name="ref" use="optional" type="xs:anyURI"/>
+ <xs:attribute name="value" use="optional"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="sm">
+ <!-- Start Annotation Marker -->
+ <xs:complexType mixed="false">
+ <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+ <xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
+ <xs:attribute name="type" use="optional" type="xlf:attrType_typeForMrk"/>
+ <xs:attribute name="ref" use="optional" type="xs:anyURI"/>
+ <xs:attribute name="value" use="optional"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="em">
+ <!-- End Annotation Marker -->
+ <xs:complexType mixed="false">
+ <xs:attribute name="startRef" use="required" type="xs:NMTOKEN"/>
+ </xs:complexType>
+ </xs:element>
+
+</xs:schema>
--- /dev/null
+<?xml version='1.0'?>
+<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
+<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns ="http://www.w3.org/1999/xhtml"
+ xml:lang="en">
+
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+ <h1>About the XML namespace</h1>
+
+ <div class="bodytext">
+ <p>
+
+ This schema document describes the XML namespace, in a form
+ suitable for import by other schema documents.
+ </p>
+ <p>
+ See <a href="http://www.w3.org/XML/1998/namespace.html">
+ http://www.w3.org/XML/1998/namespace.html</a> and
+ <a href="http://www.w3.org/TR/REC-xml">
+ http://www.w3.org/TR/REC-xml</a> for information
+ about this namespace.
+ </p>
+
+ <p>
+ Note that local names in this namespace are intended to be
+ defined only by the World Wide Web Consortium or its subgroups.
+ The names currently defined in this namespace are listed below.
+ They should not be used with conflicting semantics by any Working
+ Group, specification, or document instance.
+ </p>
+ <p>
+ See further below in this document for more information about <a
+ href="#usage">how to refer to this schema document from your own
+ XSD schema documents</a> and about <a href="#nsversioning">the
+ namespace-versioning policy governing this schema document</a>.
+ </p>
+ </div>
+ </div>
+
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:attribute name="lang">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>lang (as an attribute name)</h3>
+ <p>
+
+ denotes an attribute whose value
+ is a language code for the natural language of the content of
+ any element; its value is inherited. This name is reserved
+ by virtue of its definition in the XML specification.</p>
+
+ </div>
+ <div>
+ <h4>Notes</h4>
+ <p>
+ Attempting to install the relevant ISO 2- and 3-letter
+ codes as the enumerated possible values is probably never
+ going to be a realistic possibility.
+ </p>
+ <p>
+
+ See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
+ http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
+ and the IANA language subtag registry at
+ <a href="http://www.iana.org/assignments/language-subtag-registry">
+ http://www.iana.org/assignments/language-subtag-registry</a>
+ for further information.
+ </p>
+ <p>
+
+ The union allows for the 'un-declaration' of xml:lang with
+ the empty string.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:union memberTypes="xs:language">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value=""/>
+
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:union>
+ </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="space">
+ <xs:annotation>
+ <xs:documentation>
+
+ <div>
+
+ <h3>space (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose
+ value is a keyword indicating what whitespace processing
+ discipline is intended for the content of the element; its
+ value is inherited. This name is reserved by virtue of its
+ definition in the XML specification.</p>
+
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+
+ <xs:restriction base="xs:NCName">
+ <xs:enumeration value="default"/>
+ <xs:enumeration value="preserve"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
+ <xs:documentation>
+
+ <div>
+
+ <h3>base (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose value
+ provides a URI to be used as the base for interpreting any
+ relative URIs in the scope of the element on which it
+ appears; its value is inherited. This name is reserved
+ by virtue of its definition in the XML Base specification.</p>
+
+ <p>
+ See <a
+ href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
+ for information about this attribute.
+ </p>
+
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="id" type="xs:ID">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>id (as an attribute name)</h3>
+ <p>
+
+ denotes an attribute whose value
+ should be interpreted as if declared to be of type ID.
+ This name is reserved by virtue of its definition in the
+ xml:id specification.</p>
+
+ <p>
+ See <a
+ href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
+ for information about this attribute.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+ </xs:attribute>
+
+ <xs:attributeGroup name="specialAttrs">
+ <xs:attribute ref="xml:base"/>
+ <xs:attribute ref="xml:lang"/>
+ <xs:attribute ref="xml:space"/>
+ <xs:attribute ref="xml:id"/>
+ </xs:attributeGroup>
+
+ <xs:annotation>
+
+ <xs:documentation>
+ <div>
+
+ <h3>Father (in any context at all)</h3>
+
+ <div class="bodytext">
+ <p>
+ denotes Jon Bosak, the chair of
+ the original XML Working Group. This name is reserved by
+ the following decision of the W3C XML Plenary and
+ XML Coordination groups:
+ </p>
+ <blockquote>
+ <p>
+
+ In appreciation for his vision, leadership and
+ dedication the W3C XML Plenary on this 10th day of
+ February, 2000, reserves for Jon Bosak in perpetuity
+ the XML name "xml:Father".
+ </p>
+ </blockquote>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+ <xs:documentation>
+
+ <div xml:id="usage" id="usage">
+ <h2><a name="usage">About this schema document</a></h2>
+
+ <div class="bodytext">
+ <p>
+ This schema defines attributes and an attribute group suitable
+ for use by schemas wishing to allow <code>xml:base</code>,
+ <code>xml:lang</code>, <code>xml:space</code> or
+ <code>xml:id</code> attributes on elements they define.
+ </p>
+
+ <p>
+ To enable this, such a schema must import this schema for
+ the XML namespace, e.g. as follows:
+ </p>
+ <pre>
+ <schema.. .>
+ .. .
+ <import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+ </pre>
+ <p>
+ or
+ </p>
+ <pre>
+
+ <import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+ </pre>
+ <p>
+ Subsequently, qualified reference to any of the attributes or the
+ group defined below will have the desired effect, e.g.
+ </p>
+ <pre>
+ <type.. .>
+ .. .
+ <attributeGroup ref="xml:specialAttrs"/>
+ </pre>
+ <p>
+ will define a type which will schema-validate an instance element
+ with any of those attributes.
+ </p>
+
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+ <xs:documentation>
+ <div id="nsversioning" xml:id="nsversioning">
+ <h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
+
+ <div class="bodytext">
+ <p>
+ In keeping with the XML Schema WG's standard versioning
+ policy, this schema document will persist at
+ <a href="http://www.w3.org/2009/01/xml.xsd">
+ http://www.w3.org/2009/01/xml.xsd</a>.
+ </p>
+ <p>
+ At the date of issue it can also be found at
+ <a href="http://www.w3.org/2001/xml.xsd">
+ http://www.w3.org/2001/xml.xsd</a>.
+ </p>
+
+ <p>
+ The schema document at that URI may however change in the future,
+ in order to remain compatible with the latest version of XML
+ Schema itself, or with the XML namespace itself. In other words,
+ if the XML Schema or XML namespaces change, the version of this
+ document at <a href="http://www.w3.org/2001/xml.xsd">
+ http://www.w3.org/2001/xml.xsd
+ </a>
+ will change accordingly; the version at
+ <a href="http://www.w3.org/2009/01/xml.xsd">
+ http://www.w3.org/2009/01/xml.xsd
+ </a>
+ will not change.
+ </p>
+ <p>
+
+ Previous dated (and unchanging) versions of this schema
+ document are at:
+ </p>
+ <ul>
+ <li><a href="http://www.w3.org/2009/01/xml.xsd">
+ http://www.w3.org/2009/01/xml.xsd</a></li>
+ <li><a href="http://www.w3.org/2007/08/xml.xsd">
+ http://www.w3.org/2007/08/xml.xsd</a></li>
+ <li><a href="http://www.w3.org/2004/10/xml.xsd">
+
+ http://www.w3.org/2004/10/xml.xsd</a></li>
+ <li><a href="http://www.w3.org/2001/03/xml.xsd">
+ http://www.w3.org/2001/03/xml.xsd</a></li>
+ </ul>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+</xs:schema>
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Psr\Log\LoggerInterface;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface
+{
+ /**
+ * @var TranslatorInterface|TranslatorBagInterface
+ */
+ private $translator;
+
+ /**
+ * @var LoggerInterface
+ */
+ private $logger;
+
+ /**
+ * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface
+ * @param LoggerInterface $logger
+ */
+ public function __construct(TranslatorInterface $translator, LoggerInterface $logger)
+ {
+ if (!$translator instanceof TranslatorBagInterface) {
+ throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', get_class($translator)));
+ }
+
+ $this->translator = $translator;
+ $this->logger = $logger;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function trans($id, array $parameters = array(), $domain = null, $locale = null)
+ {
+ $trans = $this->translator->trans($id, $parameters, $domain, $locale);
+ $this->log($id, $domain, $locale);
+
+ return $trans;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
+ {
+ $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
+ $this->log($id, $domain, $locale);
+
+ return $trans;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLocale($locale)
+ {
+ $this->translator->setLocale($locale);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLocale()
+ {
+ return $this->translator->getLocale();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCatalogue($locale = null)
+ {
+ return $this->translator->getCatalogue($locale);
+ }
+
+ /**
+ * Gets the fallback locales.
+ *
+ * @return array $locales The fallback locales
+ */
+ public function getFallbackLocales()
+ {
+ if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
+ return $this->translator->getFallbackLocales();
+ }
+
+ return array();
+ }
+
+ /**
+ * Passes through all unknown calls onto the translator object.
+ */
+ public function __call($method, $args)
+ {
+ return call_user_func_array(array($this->translator, $method), $args);
+ }
+
+ /**
+ * Logs for missing translations.
+ *
+ * @param string $id
+ * @param string|null $domain
+ * @param string|null $locale
+ */
+ private function log($id, $domain, $locale)
+ {
+ if (null === $domain) {
+ $domain = 'messages';
+ }
+
+ $id = (string) $id;
+ $catalogue = $this->translator->getCatalogue($locale);
+ if ($catalogue->defines($id, $domain)) {
+ return;
+ }
+
+ if ($catalogue->has($id, $domain)) {
+ $this->logger->debug('Translation use fallback catalogue.', array('id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()));
+ } else {
+ $this->logger->warning('Translation not found.', array('id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()));
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Config\Resource\ResourceInterface;
+use Symfony\Component\Translation\Exception\LogicException;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface
+{
+ private $messages = array();
+ private $metadata = array();
+ private $resources = array();
+ private $locale;
+ private $fallbackCatalogue;
+ private $parent;
+
+ /**
+ * @param string $locale The locale
+ * @param array $messages An array of messages classified by domain
+ */
+ public function __construct($locale, array $messages = array())
+ {
+ $this->locale = $locale;
+ $this->messages = $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLocale()
+ {
+ return $this->locale;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDomains()
+ {
+ return array_keys($this->messages);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all($domain = null)
+ {
+ if (null === $domain) {
+ return $this->messages;
+ }
+
+ return isset($this->messages[$domain]) ? $this->messages[$domain] : array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($id, $translation, $domain = 'messages')
+ {
+ $this->add(array($id => $translation), $domain);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($id, $domain = 'messages')
+ {
+ if (isset($this->messages[$domain][$id])) {
+ return true;
+ }
+
+ if (null !== $this->fallbackCatalogue) {
+ return $this->fallbackCatalogue->has($id, $domain);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function defines($id, $domain = 'messages')
+ {
+ return isset($this->messages[$domain][$id]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($id, $domain = 'messages')
+ {
+ if (isset($this->messages[$domain][$id])) {
+ return $this->messages[$domain][$id];
+ }
+
+ if (null !== $this->fallbackCatalogue) {
+ return $this->fallbackCatalogue->get($id, $domain);
+ }
+
+ return $id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace($messages, $domain = 'messages')
+ {
+ $this->messages[$domain] = array();
+
+ $this->add($messages, $domain);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add($messages, $domain = 'messages')
+ {
+ if (!isset($this->messages[$domain])) {
+ $this->messages[$domain] = $messages;
+ } else {
+ $this->messages[$domain] = array_replace($this->messages[$domain], $messages);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addCatalogue(MessageCatalogueInterface $catalogue)
+ {
+ if ($catalogue->getLocale() !== $this->locale) {
+ throw new LogicException(sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s"', $catalogue->getLocale(), $this->locale));
+ }
+
+ foreach ($catalogue->all() as $domain => $messages) {
+ $this->add($messages, $domain);
+ }
+
+ foreach ($catalogue->getResources() as $resource) {
+ $this->addResource($resource);
+ }
+
+ if ($catalogue instanceof MetadataAwareInterface) {
+ $metadata = $catalogue->getMetadata('', '');
+ $this->addMetadata($metadata);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addFallbackCatalogue(MessageCatalogueInterface $catalogue)
+ {
+ // detect circular references
+ $c = $catalogue;
+ while ($c = $c->getFallbackCatalogue()) {
+ if ($c->getLocale() === $this->getLocale()) {
+ throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
+ }
+ }
+
+ $c = $this;
+ do {
+ if ($c->getLocale() === $catalogue->getLocale()) {
+ throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
+ }
+
+ foreach ($catalogue->getResources() as $resource) {
+ $c->addResource($resource);
+ }
+ } while ($c = $c->parent);
+
+ $catalogue->parent = $this;
+ $this->fallbackCatalogue = $catalogue;
+
+ foreach ($catalogue->getResources() as $resource) {
+ $this->addResource($resource);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFallbackCatalogue()
+ {
+ return $this->fallbackCatalogue;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getResources()
+ {
+ return array_values($this->resources);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addResource(ResourceInterface $resource)
+ {
+ $this->resources[$resource->__toString()] = $resource;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMetadata($key = '', $domain = 'messages')
+ {
+ if ('' == $domain) {
+ return $this->metadata;
+ }
+
+ if (isset($this->metadata[$domain])) {
+ if ('' == $key) {
+ return $this->metadata[$domain];
+ }
+
+ if (isset($this->metadata[$domain][$key])) {
+ return $this->metadata[$domain][$key];
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setMetadata($key, $value, $domain = 'messages')
+ {
+ $this->metadata[$domain][$key] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function deleteMetadata($key = '', $domain = 'messages')
+ {
+ if ('' == $domain) {
+ $this->metadata = array();
+ } elseif ('' == $key) {
+ unset($this->metadata[$domain]);
+ } else {
+ unset($this->metadata[$domain][$key]);
+ }
+ }
+
+ /**
+ * Adds current values with the new values.
+ *
+ * @param array $values Values to add
+ */
+ private function addMetadata(array $values)
+ {
+ foreach ($values as $domain => $keys) {
+ foreach ($keys as $key => $value) {
+ $this->setMetadata($key, $value, $domain);
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Config\Resource\ResourceInterface;
+
+/**
+ * MessageCatalogueInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface MessageCatalogueInterface
+{
+ /**
+ * Gets the catalogue locale.
+ *
+ * @return string The locale
+ */
+ public function getLocale();
+
+ /**
+ * Gets the domains.
+ *
+ * @return array An array of domains
+ */
+ public function getDomains();
+
+ /**
+ * Gets the messages within a given domain.
+ *
+ * If $domain is null, it returns all messages.
+ *
+ * @param string $domain The domain name
+ *
+ * @return array An array of messages
+ */
+ public function all($domain = null);
+
+ /**
+ * Sets a message translation.
+ *
+ * @param string $id The message id
+ * @param string $translation The messages translation
+ * @param string $domain The domain name
+ */
+ public function set($id, $translation, $domain = 'messages');
+
+ /**
+ * Checks if a message has a translation.
+ *
+ * @param string $id The message id
+ * @param string $domain The domain name
+ *
+ * @return bool true if the message has a translation, false otherwise
+ */
+ public function has($id, $domain = 'messages');
+
+ /**
+ * Checks if a message has a translation (it does not take into account the fallback mechanism).
+ *
+ * @param string $id The message id
+ * @param string $domain The domain name
+ *
+ * @return bool true if the message has a translation, false otherwise
+ */
+ public function defines($id, $domain = 'messages');
+
+ /**
+ * Gets a message translation.
+ *
+ * @param string $id The message id
+ * @param string $domain The domain name
+ *
+ * @return string The message translation
+ */
+ public function get($id, $domain = 'messages');
+
+ /**
+ * Sets translations for a given domain.
+ *
+ * @param array $messages An array of translations
+ * @param string $domain The domain name
+ */
+ public function replace($messages, $domain = 'messages');
+
+ /**
+ * Adds translations for a given domain.
+ *
+ * @param array $messages An array of translations
+ * @param string $domain The domain name
+ */
+ public function add($messages, $domain = 'messages');
+
+ /**
+ * Merges translations from the given Catalogue into the current one.
+ *
+ * The two catalogues must have the same locale.
+ *
+ * @param self $catalogue
+ */
+ public function addCatalogue(MessageCatalogueInterface $catalogue);
+
+ /**
+ * Merges translations from the given Catalogue into the current one
+ * only when the translation does not exist.
+ *
+ * This is used to provide default translations when they do not exist for the current locale.
+ *
+ * @param self $catalogue
+ */
+ public function addFallbackCatalogue(MessageCatalogueInterface $catalogue);
+
+ /**
+ * Gets the fallback catalogue.
+ *
+ * @return self|null A MessageCatalogueInterface instance or null when no fallback has been set
+ */
+ public function getFallbackCatalogue();
+
+ /**
+ * Returns an array of resources loaded to build this collection.
+ *
+ * @return ResourceInterface[] An array of resources
+ */
+ public function getResources();
+
+ /**
+ * Adds a resource for this collection.
+ *
+ * @param ResourceInterface $resource A resource instance
+ */
+ public function addResource(ResourceInterface $resource);
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * MessageSelector.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class MessageSelector
+{
+ /**
+ * Given a message with different plural translations separated by a
+ * pipe (|), this method returns the correct portion of the message based
+ * on the given number, locale and the pluralization rules in the message
+ * itself.
+ *
+ * The message supports two different types of pluralization rules:
+ *
+ * interval: {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples
+ * indexed: There is one apple|There are %count% apples
+ *
+ * The indexed solution can also contain labels (e.g. one: There is one apple).
+ * This is purely for making the translations more clear - it does not
+ * affect the functionality.
+ *
+ * The two methods can also be mixed:
+ * {0} There are no apples|one: There is one apple|more: There are %count% apples
+ *
+ * @param string $message The message being translated
+ * @param int $number The number of items represented for the message
+ * @param string $locale The locale to use for choosing
+ *
+ * @return string
+ *
+ * @throws InvalidArgumentException
+ */
+ public function choose($message, $number, $locale)
+ {
+ preg_match_all('/(?:\|\||[^\|])++/', $message, $parts);
+ $explicitRules = array();
+ $standardRules = array();
+ foreach ($parts[0] as $part) {
+ $part = trim(str_replace('||', '|', $part));
+
+ if (preg_match('/^(?P<interval>'.Interval::getIntervalRegexp().')\s*(?P<message>.*?)$/xs', $part, $matches)) {
+ $explicitRules[$matches['interval']] = $matches['message'];
+ } elseif (preg_match('/^\w+\:\s*(.*?)$/', $part, $matches)) {
+ $standardRules[] = $matches[1];
+ } else {
+ $standardRules[] = $part;
+ }
+ }
+
+ // try to match an explicit rule, then fallback to the standard ones
+ foreach ($explicitRules as $interval => $m) {
+ if (Interval::test($number, $interval)) {
+ return $m;
+ }
+ }
+
+ $position = PluralizationRules::get($number, $locale);
+
+ if (!isset($standardRules[$position])) {
+ // when there's exactly one rule given, and that rule is a standard
+ // rule, use this rule
+ if (1 === count($parts[0]) && isset($standardRules[0])) {
+ return $standardRules[0];
+ }
+
+ throw new InvalidArgumentException(sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $message, $locale, $number));
+ }
+
+ return $standardRules[$position];
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+/**
+ * MetadataAwareInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface MetadataAwareInterface
+{
+ /**
+ * Gets metadata for the given domain and key.
+ *
+ * Passing an empty domain will return an array with all metadata indexed by
+ * domain and then by key. Passing an empty key will return an array with all
+ * metadata for the given domain.
+ *
+ * @param string $key The key
+ * @param string $domain The domain name
+ *
+ * @return mixed The value that was set or an array with the domains/keys or null
+ */
+ public function getMetadata($key = '', $domain = 'messages');
+
+ /**
+ * Adds metadata to a message domain.
+ *
+ * @param string $key The key
+ * @param mixed $value The value
+ * @param string $domain The domain name
+ */
+ public function setMetadata($key, $value, $domain = 'messages');
+
+ /**
+ * Deletes metadata for the given key and domain.
+ *
+ * Passing an empty domain will delete all metadata. Passing an empty key will
+ * delete all metadata for the given domain.
+ *
+ * @param string $key The key
+ * @param string $domain The domain name
+ */
+ public function deleteMetadata($key = '', $domain = 'messages');
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+/**
+ * Returns the plural rules for a given locale.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class PluralizationRules
+{
+ private static $rules = array();
+
+ /**
+ * Returns the plural position to use for the given locale and number.
+ *
+ * @param int $number The number
+ * @param string $locale The locale
+ *
+ * @return int The plural position
+ */
+ public static function get($number, $locale)
+ {
+ if ('pt_BR' === $locale) {
+ // temporary set a locale for brazilian
+ $locale = 'xbr';
+ }
+
+ if (strlen($locale) > 3) {
+ $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
+ }
+
+ if (isset(self::$rules[$locale])) {
+ $return = call_user_func(self::$rules[$locale], $number);
+
+ if (!is_int($return) || $return < 0) {
+ return 0;
+ }
+
+ return $return;
+ }
+
+ /*
+ * The plural rules are derived from code of the Zend Framework (2010-09-25),
+ * which is subject to the new BSD license (http://framework.zend.com/license/new-bsd).
+ * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ */
+ switch ($locale) {
+ case 'az':
+ case 'bo':
+ case 'dz':
+ case 'id':
+ case 'ja':
+ case 'jv':
+ case 'ka':
+ case 'km':
+ case 'kn':
+ case 'ko':
+ case 'ms':
+ case 'th':
+ case 'tr':
+ case 'vi':
+ case 'zh':
+ return 0;
+ break;
+
+ case 'af':
+ case 'bn':
+ case 'bg':
+ case 'ca':
+ case 'da':
+ case 'de':
+ case 'el':
+ case 'en':
+ case 'eo':
+ case 'es':
+ case 'et':
+ case 'eu':
+ case 'fa':
+ case 'fi':
+ case 'fo':
+ case 'fur':
+ case 'fy':
+ case 'gl':
+ case 'gu':
+ case 'ha':
+ case 'he':
+ case 'hu':
+ case 'is':
+ case 'it':
+ case 'ku':
+ case 'lb':
+ case 'ml':
+ case 'mn':
+ case 'mr':
+ case 'nah':
+ case 'nb':
+ case 'ne':
+ case 'nl':
+ case 'nn':
+ case 'no':
+ case 'om':
+ case 'or':
+ case 'pa':
+ case 'pap':
+ case 'ps':
+ case 'pt':
+ case 'so':
+ case 'sq':
+ case 'sv':
+ case 'sw':
+ case 'ta':
+ case 'te':
+ case 'tk':
+ case 'ur':
+ case 'zu':
+ return (1 == $number) ? 0 : 1;
+
+ case 'am':
+ case 'bh':
+ case 'fil':
+ case 'fr':
+ case 'gun':
+ case 'hi':
+ case 'hy':
+ case 'ln':
+ case 'mg':
+ case 'nso':
+ case 'xbr':
+ case 'ti':
+ case 'wa':
+ return ((0 == $number) || (1 == $number)) ? 0 : 1;
+
+ case 'be':
+ case 'bs':
+ case 'hr':
+ case 'ru':
+ case 'sr':
+ case 'uk':
+ return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+
+ case 'cs':
+ case 'sk':
+ return (1 == $number) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
+
+ case 'ga':
+ return (1 == $number) ? 0 : ((2 == $number) ? 1 : 2);
+
+ case 'lt':
+ return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+
+ case 'sl':
+ return (1 == $number % 100) ? 0 : ((2 == $number % 100) ? 1 : (((3 == $number % 100) || (4 == $number % 100)) ? 2 : 3));
+
+ case 'mk':
+ return (1 == $number % 10) ? 0 : 1;
+
+ case 'mt':
+ return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
+
+ case 'lv':
+ return (0 == $number) ? 0 : (((1 == $number % 10) && (11 != $number % 100)) ? 1 : 2);
+
+ case 'pl':
+ return (1 == $number) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
+
+ case 'cy':
+ return (1 == $number) ? 0 : ((2 == $number) ? 1 : (((8 == $number) || (11 == $number)) ? 2 : 3));
+
+ case 'ro':
+ return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
+
+ case 'ar':
+ return (0 == $number) ? 0 : ((1 == $number) ? 1 : ((2 == $number) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5))));
+
+ default:
+ return 0;
+ }
+ }
+
+ /**
+ * Overrides the default plural rule for a given locale.
+ *
+ * @param callable $rule A PHP callable
+ * @param string $locale The locale
+ */
+ public static function set(callable $rule, $locale)
+ {
+ if ('pt_BR' === $locale) {
+ // temporary set a locale for brazilian
+ $locale = 'xbr';
+ }
+
+ if (strlen($locale) > 3) {
+ $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
+ }
+
+ self::$rules[$locale] = $rule;
+ }
+}
--- /dev/null
+Translation Component
+=====================
+
+The Translation component provides tools to internationalize your application.
+
+Resources
+---------
+
+ * [Documentation](https://symfony.com/doc/current/components/translation/index.html)
+ * [Contributing](https://symfony.com/doc/current/contributing/index.html)
+ * [Report issues](https://github.com/symfony/symfony/issues) and
+ [send Pull Requests](https://github.com/symfony/symfony/pulls)
+ in the [main Symfony repository](https://github.com/symfony/symfony)
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+
+May-19-2004:
+- Changed the <choice> for ElemType_header, moving minOccurs="0" maxOccurs="unbounded" from its elements
+to <choice> itself.
+- Added <choice> for ElemType_trans-unit to allow "any order" for <context-group>, <count-group>, <prop-group>, <note>, and
+<alt-trans>.
+
+Oct-2005
+- updated version info to 1.2
+- equiv-trans attribute to <trans-unit> element
+- merged-trans attribute for <group> element
+- Add the <seg-source> element as optional in the <trans-unit> and <alt-trans> content models, at the same level as <source>
+- Create a new value "seg" for the mtype attribute of the <mrk> element
+- Add mid as an optional attribute for the <alt-trans> element
+
+Nov-14-2005
+- Changed name attribute for <context-group> from required to optional
+- Added extension point at <xliff>
+
+Jan-9-2006
+- Added alttranstype type attribute to <alt-trans>, and values
+
+Jan-10-2006
+- Corrected error with overwritten purposeValueList
+- Corrected name="AttrType_Version", attribute should have been "name"
+
+-->
+<xsd:schema xmlns:xlf="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:oasis:names:tc:xliff:document:1.2" xml:lang="en">
+ <!-- Import for xml:lang and xml:space -->
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+ <!-- Attributes Lists -->
+ <xsd:simpleType name="XTend">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="x-[^\s]+"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="context-typeValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'context-type'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="database">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a database content.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="element">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the content of an element within an XML document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="elementtitle">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the name of an element within an XML document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="linenumber">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the line number from the sourcefile (see context-type="sourcefile") where the <source> is found.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="numparams">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a the number of parameters contained within the <source>.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="paramnotes">
+ <xsd:annotation>
+ <xsd:documentation>Indicates notes pertaining to the parameters in the <source>.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="record">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the content of a record within a database.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="recordtitle">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the name of a record within a database.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="sourcefile">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the original source file in the case that multiple files are merged to form the original file from which the XLIFF file is created. This differs from the original <file> attribute in that this sourcefile is one of many that make up that file.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="count-typeValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'count-type'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="num-usages">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the count units are items that are used X times in a certain context; example: this is a reusable text unit which is used 42 times in other texts.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="repetition">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the count units are translation units existing already in the same document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="total">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a total count.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="InlineDelimitersValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'ctype' when used other elements than <ph> or <x>.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="bold">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a run of bolded text.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="italic">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a run of text in italics.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="underlined">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a run of underlined text.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="link">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a run of hyper-text.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="InlinePlaceholdersValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'ctype' when used with <ph> or <x>.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="image">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a inline image.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pb">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a page break.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="lb">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a line break.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="mime-typeValueList">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="(text|multipart|message|application|image|audio|video|model)(/.+)*"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="datatypeValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'datatype'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="asp">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Active Server Page data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="c">
+ <xsd:annotation>
+ <xsd:documentation>Indicates C source file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cdf">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Channel Definition Format (CDF) data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cfm">
+ <xsd:annotation>
+ <xsd:documentation>Indicates ColdFusion data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cpp">
+ <xsd:annotation>
+ <xsd:documentation>Indicates C++ source file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="csharp">
+ <xsd:annotation>
+ <xsd:documentation>Indicates C-Sharp data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cstring">
+ <xsd:annotation>
+ <xsd:documentation>Indicates strings from C, ASM, and driver files data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="csv">
+ <xsd:annotation>
+ <xsd:documentation>Indicates comma-separated values data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="database">
+ <xsd:annotation>
+ <xsd:documentation>Indicates database data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="documentfooter">
+ <xsd:annotation>
+ <xsd:documentation>Indicates portions of document that follows data and contains metadata.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="documentheader">
+ <xsd:annotation>
+ <xsd:documentation>Indicates portions of document that precedes data and contains metadata.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="filedialog">
+ <xsd:annotation>
+ <xsd:documentation>Indicates data from standard UI file operations dialogs (e.g., Open, Save, Save As, Export, Import).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="form">
+ <xsd:annotation>
+ <xsd:documentation>Indicates standard user input screen data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="html">
+ <xsd:annotation>
+ <xsd:documentation>Indicates HyperText Markup Language (HTML) data - document instance.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="htmlbody">
+ <xsd:annotation>
+ <xsd:documentation>Indicates content within an HTML document’s <body> element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="ini">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Windows INI file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="interleaf">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Interleaf data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="javaclass">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Java source file data (extension '.java').</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="javapropertyresourcebundle">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Java property resource bundle data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="javalistresourcebundle">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Java list resource bundle data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="javascript">
+ <xsd:annotation>
+ <xsd:documentation>Indicates JavaScript source file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="jscript">
+ <xsd:annotation>
+ <xsd:documentation>Indicates JScript source file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="layout">
+ <xsd:annotation>
+ <xsd:documentation>Indicates information relating to formatting.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="lisp">
+ <xsd:annotation>
+ <xsd:documentation>Indicates LISP source file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="margin">
+ <xsd:annotation>
+ <xsd:documentation>Indicates information relating to margin formats.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="menufile">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a file containing menu.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="messagefile">
+ <xsd:annotation>
+ <xsd:documentation>Indicates numerically identified string table.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="mif">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Maker Interchange Format (MIF) data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="mimetype">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the datatype attribute value is a MIME Type value and is defined in the mime-type attribute.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="mo">
+ <xsd:annotation>
+ <xsd:documentation>Indicates GNU Machine Object data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="msglib">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Message Librarian strings created by Novell's Message Librarian Tool.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pagefooter">
+ <xsd:annotation>
+ <xsd:documentation>Indicates information to be displayed at the bottom of each page of a document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pageheader">
+ <xsd:annotation>
+ <xsd:documentation>Indicates information to be displayed at the top of each page of a document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="parameters">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a list of property values (e.g., settings within INI files or preferences dialog).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pascal">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Pascal source file data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="php">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Hypertext Preprocessor data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="plaintext">
+ <xsd:annotation>
+ <xsd:documentation>Indicates plain text file (no formatting other than, possibly, wrapping).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="po">
+ <xsd:annotation>
+ <xsd:documentation>Indicates GNU Portable Object file.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="report">
+ <xsd:annotation>
+ <xsd:documentation>Indicates dynamically generated user defined document. e.g. Oracle Report, Crystal Report, etc.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="resources">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Windows .NET binary resources.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="resx">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Windows .NET Resources.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rtf">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Rich Text Format (RTF) data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="sgml">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Standard Generalized Markup Language (SGML) data - document instance.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="sgmldtd">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Standard Generalized Markup Language (SGML) data - Document Type Definition (DTD).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="svg">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Scalable Vector Graphic (SVG) data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="vbscript">
+ <xsd:annotation>
+ <xsd:documentation>Indicates VisualBasic Script source file.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="warning">
+ <xsd:annotation>
+ <xsd:documentation>Indicates warning message.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="winres">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Windows (Win32) resources (i.e. resources extracted from an RC script, a message file, or a compiled file).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="xhtml">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Extensible HyperText Markup Language (XHTML) data - document instance.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="xml">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Extensible Markup Language (XML) data - document instance.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="xmldtd">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Extensible Markup Language (XML) data - Document Type Definition (DTD).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="xsl">
+ <xsd:annotation>
+ <xsd:documentation>Indicates Extensible Stylesheet Language (XSL) data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="xul">
+ <xsd:annotation>
+ <xsd:documentation>Indicates XUL elements.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="mtypeValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'mtype'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="abbrev">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the marked text is an abbreviation.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="abbreviated-form">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.8: A term resulting from the omission of any part of the full term while designating the same concept.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="abbreviation">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.8.1: An abbreviated form of a simple term resulting from the omission of some of its letters (e.g. 'adj.' for 'adjective').</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="acronym">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.8.4: An abbreviated form of a term made up of letters from the full form of a multiword term strung together into a sequence pronounced only syllabically (e.g. 'radar' for 'radio detecting and ranging').</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="appellation">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620: A proper-name term, such as the name of an agency or other proper entity.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="collocation">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.18.1: A recurrent word combination characterized by cohesion in that the components of the collocation must co-occur within an utterance or series of utterances, even though they do not necessarily have to maintain immediate proximity to one another.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="common-name">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.5: A synonym for an international scientific term that is used in general discourse in a given language.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="datetime">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the marked text is a date and/or time.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="equation">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.15: An expression used to represent a concept based on a statement that two mathematical expressions are, for instance, equal as identified by the equal sign (=), or assigned to one another by a similar sign.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="expanded-form">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.7: The complete representation of a term for which there is an abbreviated form.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="formula">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.14: Figures, symbols or the like used to express a concept briefly, such as a mathematical or chemical formula.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="head-term">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.1: The concept designation that has been chosen to head a terminological record.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="initialism">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.8.3: An abbreviated form of a term consisting of some of the initial letters of the words making up a multiword term or the term elements making up a compound term when these letters are pronounced individually (e.g. 'BSE' for 'bovine spongiform encephalopathy').</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="international-scientific-term">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.4: A term that is part of an international scientific nomenclature as adopted by an appropriate scientific body.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="internationalism">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.6: A term that has the same or nearly identical orthographic or phonemic form in many languages.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="logical-expression">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.16: An expression used to represent a concept based on mathematical or logical relations, such as statements of inequality, set relationships, Boolean operations, and the like.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="materials-management-unit">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.17: A unit to track object.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="name">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the marked text is a name.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="near-synonym">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.3: A term that represents the same or a very similar concept as another term in the same language, but for which interchangeability is limited to some contexts and inapplicable in others.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="part-number">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.17.2: A unique alphanumeric designation assigned to an object in a manufacturing system.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="phrase">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the marked text is a phrase.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="phraseological-unit">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.18: Any group of two or more words that form a unit, the meaning of which frequently cannot be deduced based on the combined sense of the words making up the phrase.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="protected">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the marked text should not be translated.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="romanized-form">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.12: A form of a term resulting from an operation whereby non-Latin writing systems are converted to the Latin alphabet.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="seg">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the marked text represents a segment.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="set-phrase">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.18.2: A fixed, lexicalized phrase.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="short-form">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.8.2: A variant of a multiword term that includes fewer words than the full form of the term (e.g. 'Group of Twenty-four' for 'Intergovernmental Group of Twenty-four on International Monetary Affairs').</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="sku">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.17.1: Stock keeping unit, an inventory item identified by a unique alphanumeric designation assigned to an object in an inventory control system.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="standard-text">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.19: A fixed chunk of recurring text.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="symbol">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.13: A designation of a concept by letters, numerals, pictograms or any combination thereof.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="synonym">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.2: Any term that represents the same or a very similar concept as the main entry term in a term entry.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="synonymous-phrase">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.18.3: Phraseological unit in a language that expresses the same semantic content as another phrase in that same language.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="term">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the marked text is a term.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="transcribed-form">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.11: A form of a term resulting from an operation whereby the characters of one writing system are represented by characters from another writing system, taking into account the pronunciation of the characters converted.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="transliterated-form">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.10: A form of a term resulting from an operation whereby the characters of an alphabetic writing system are represented by characters from another alphabetic writing system.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="truncated-term">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.8.5: An abbreviated form of a term resulting from the omission of one or more term elements or syllables (e.g. 'flu' for 'influenza').</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="variant">
+ <xsd:annotation>
+ <xsd:documentation>ISO-12620 2.1.9: One of the alternate forms of a term.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="restypeValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'restype'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="auto3state">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC AUTO3STATE control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="autocheckbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC AUTOCHECKBOX control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="autoradiobutton">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC AUTORADIOBUTTON control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="bedit">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC BEDIT control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="bitmap">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a bitmap, for example a BITMAP resource in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="button">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a button object, for example a BUTTON control Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="caption">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a caption, such as the caption of a dialog box.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cell">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the cell in a table, for example the content of the <td> element in HTML.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="checkbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates check box object, for example a CHECKBOX control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="checkboxmenuitem">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a menu item with an associated checkbox.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="checkedlistbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a list box, but with a check-box for each item.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="colorchooser">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a color selection dialog.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="combobox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a combination of edit box and listbox object, for example a COMBOBOX control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="comboboxexitem">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an initialization entry of an extended combobox DLGINIT resource block. (code 0x1234).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="comboboxitem">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an initialization entry of a combobox DLGINIT resource block (code 0x0403).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="component">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a UI base class element that cannot be represented by any other element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="contextmenu">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a context menu.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="ctext">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC CTEXT control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cursor">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a cursor, for example a CURSOR resource in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="datetimepicker">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a date/time picker.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="defpushbutton">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC DEFPUSHBUTTON control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="dialog">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a dialog box.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="dlginit">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC DLGINIT resource block.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="edit">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an edit box object, for example an EDIT control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="file">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a filename.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="filechooser">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a file dialog.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="fn">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a footnote.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="font">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a font name.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="footer">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a footer.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="frame">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a frame object.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="grid">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a XUL grid element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="groupbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a groupbox object, for example a GROUPBOX control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="header">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a header item.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="heading">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a heading, such has the content of <h1>, <h2>, etc. in HTML.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="hedit">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC HEDIT control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="hscrollbar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a horizontal scrollbar.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="icon">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an icon, for example an ICON resource in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="iedit">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC IEDIT control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="keywords">
+ <xsd:annotation>
+ <xsd:documentation>Indicates keyword list, such as the content of the Keywords meta-data in HTML, or a K footnote in WinHelp RTF.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="label">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a label object.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="linklabel">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a label that is also a HTML link (not necessarily a URL).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="list">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a list (a group of list-items, for example an <ol> or <ul> element in HTML).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="listbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a listbox object, for example an LISTBOX control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="listitem">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an list item (an entry in a list).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="ltext">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC LTEXT control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="menu">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a menu (a group of menu-items).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="menubar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a toolbar containing one or more tope level menus.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="menuitem">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a menu item (an entry in a menu).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="menuseparator">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a XUL menuseparator element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="message">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a message, for example an entry in a MESSAGETABLE resource in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="monthcalendar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a calendar control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="numericupdown">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an edit box beside a spin control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="panel">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a catch all for rectangular areas.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="popupmenu">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a standalone menu not necessarily associated with a menubar.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pushbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a pushbox object, for example a PUSHBOX control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pushbutton">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC PUSHBUTTON control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="radio">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a radio button object.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="radiobuttonmenuitem">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a menuitem with associated radio button.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rcdata">
+ <xsd:annotation>
+ <xsd:documentation>Indicates raw data resources for an application.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="row">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a row in a table.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rtext">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC RTEXT control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="scrollpane">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a user navigable container used to show a portion of a document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="separator">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a generic divider object (e.g. menu group separator).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="shortcut">
+ <xsd:annotation>
+ <xsd:documentation>Windows accelerators, shortcuts in resource or property files.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="spinner">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a UI control to indicate process activity but not progress.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="splitter">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a splitter bar.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="state3">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC STATE3 control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="statusbar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a window for providing feedback to the users, like 'read-only', etc.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="string">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a string, for example an entry in a STRINGTABLE resource in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="tabcontrol">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a layers of controls with a tab to select layers.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="table">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a display and edits regular two-dimensional tables of cells.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="textbox">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a XUL textbox element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="togglebutton">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a UI button that can be toggled to on or off state.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="toolbar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an array of controls, usually buttons.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="tooltip">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a pop up tool tip text.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="trackbar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a bar with a pointer indicating a position within a certain range.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="tree">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a control that displays a set of hierarchical data.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="uri">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a URI (URN or URL).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="userbutton">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a Windows RC USERBUTTON control.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="usercontrol">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a user-defined control like CONTROL control in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="var">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the text of a variable.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="versioninfo">
+ <xsd:annotation>
+ <xsd:documentation>Indicates version information about a resource like VERSIONINFO in Windows.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="vscrollbar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a vertical scrollbar.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="window">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a graphical window.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="size-unitValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'size-unit'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="byte">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in 8-bit bytes.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="char">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in Unicode characters.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="col">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in columns. Used for HTML text area.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="cm">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in centimeters.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="dlgunit">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in dialog units, as defined in Windows resources.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="em">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in 'font-size' units (as defined in CSS).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="ex">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in 'x-height' units (as defined in CSS).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="glyph">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in glyphs. A glyph is considered to be one or more combined Unicode characters that represent a single displayable text character. Sometimes referred to as a 'grapheme cluster'</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="in">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in inches.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="mm">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in millimeters.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="percent">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in percentage.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="pixel">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in pixels.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="point">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in point.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="row">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a size in rows. Used for HTML text area.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="stateValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'state'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="final">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the terminating state.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="needs-adaptation">
+ <xsd:annotation>
+ <xsd:documentation>Indicates only non-textual information needs adaptation.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="needs-l10n">
+ <xsd:annotation>
+ <xsd:documentation>Indicates both text and non-textual information needs adaptation.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="needs-review-adaptation">
+ <xsd:annotation>
+ <xsd:documentation>Indicates only non-textual information needs review.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="needs-review-l10n">
+ <xsd:annotation>
+ <xsd:documentation>Indicates both text and non-textual information needs review.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="needs-review-translation">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that only the text of the item needs to be reviewed.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="needs-translation">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item needs to be translated.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="new">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item is new. For example, translation units that were not in a previous version of the document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="signed-off">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that changes are reviewed and approved.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="translated">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item has been translated.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="state-qualifierValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'state-qualifier'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="exact-match">
+ <xsd:annotation>
+ <xsd:documentation>Indicates an exact match. An exact match occurs when a source text of a segment is exactly the same as the source text of a segment that was translated previously.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="fuzzy-match">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a fuzzy match. A fuzzy match occurs when a source text of a segment is very similar to the source text of a segment that was translated previously (e.g. when the difference is casing, a few changed words, white-space discripancy, etc.).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="id-match">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a match based on matching IDs (in addition to matching text).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="leveraged-glossary">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a translation derived from a glossary.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="leveraged-inherited">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a translation derived from existing translation.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="leveraged-mt">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a translation derived from machine translation.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="leveraged-repository">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a translation derived from a translation repository.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="leveraged-tm">
+ <xsd:annotation>
+ <xsd:documentation>Indicates a translation derived from a translation memory.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="mt-suggestion">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the translation is suggested by machine translation.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rejected-grammar">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item has been rejected because of incorrect grammar.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rejected-inaccurate">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item has been rejected because it is incorrect.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rejected-length">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item has been rejected because it is too long or too short.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rejected-spelling">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the item has been rejected because of incorrect spelling.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="tm-suggestion">
+ <xsd:annotation>
+ <xsd:documentation>Indicates the translation is suggested by translation memory.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="unitValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'unit'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="word">
+ <xsd:annotation>
+ <xsd:documentation>Refers to words.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="page">
+ <xsd:annotation>
+ <xsd:documentation>Refers to pages.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="trans-unit">
+ <xsd:annotation>
+ <xsd:documentation>Refers to <trans-unit> elements.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="bin-unit">
+ <xsd:annotation>
+ <xsd:documentation>Refers to <bin-unit> elements.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="glyph">
+ <xsd:annotation>
+ <xsd:documentation>Refers to glyphs.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="item">
+ <xsd:annotation>
+ <xsd:documentation>Refers to <trans-unit> and/or <bin-unit> elements.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="instance">
+ <xsd:annotation>
+ <xsd:documentation>Refers to the occurrences of instances defined by the count-type value.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="character">
+ <xsd:annotation>
+ <xsd:documentation>Refers to characters.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="line">
+ <xsd:annotation>
+ <xsd:documentation>Refers to lines.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="sentence">
+ <xsd:annotation>
+ <xsd:documentation>Refers to sentences.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="paragraph">
+ <xsd:annotation>
+ <xsd:documentation>Refers to paragraphs.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="segment">
+ <xsd:annotation>
+ <xsd:documentation>Refers to segments.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="placeable">
+ <xsd:annotation>
+ <xsd:documentation>Refers to placeables (inline elements).</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="priorityValueList">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'priority'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:positiveInteger">
+ <xsd:enumeration value="1">
+ <xsd:annotation>
+ <xsd:documentation>Highest priority.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="2">
+ <xsd:annotation>
+ <xsd:documentation>High priority.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="3">
+ <xsd:annotation>
+ <xsd:documentation>High priority, but not as important as 2.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="4">
+ <xsd:annotation>
+ <xsd:documentation>High priority, but not as important as 3.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="5">
+ <xsd:annotation>
+ <xsd:documentation>Medium priority, but more important than 6.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="6">
+ <xsd:annotation>
+ <xsd:documentation>Medium priority, but less important than 5.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="7">
+ <xsd:annotation>
+ <xsd:documentation>Low priority, but more important than 8.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="8">
+ <xsd:annotation>
+ <xsd:documentation>Low priority, but more important than 9.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="9">
+ <xsd:annotation>
+ <xsd:documentation>Low priority.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="10">
+ <xsd:annotation>
+ <xsd:documentation>Lowest priority.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="reformatValueYesNo">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="yes">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that all properties can be reformatted. This value must be used alone.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="no">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that no properties should be reformatted. This value must be used alone.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="reformatValueList">
+ <xsd:list>
+ <xsd:simpleType>
+ <xsd:union memberTypes="xlf:XTend">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="coord">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that all information in the coord attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="coord-x">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the x information in the coord attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="coord-y">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the y information in the coord attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="coord-cx">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the cx information in the coord attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="coord-cy">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the cy information in the coord attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="font">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that all the information in the font attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="font-name">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the name information in the font attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="font-size">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the size information in the font attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="font-weight">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the weight information in the font attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="css-style">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the information in the css-style attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="style">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the information in the style attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="ex-style">
+ <xsd:annotation>
+ <xsd:documentation>This value indicates that the information in the exstyle attribute can be modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:union>
+ </xsd:simpleType>
+ </xsd:list>
+ </xsd:simpleType>
+ <xsd:simpleType name="purposeValueList">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="information">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the context is informational in nature, specifying for example, how a term should be translated. Thus, should be displayed to anyone editing the XLIFF document.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="location">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the context-group is used to specify where the term was found in the translatable source. Thus, it is not displayed.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="match">
+ <xsd:annotation>
+ <xsd:documentation>Indicates that the context information should be used during translation memory lookups. Thus, it is not displayed.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="alttranstypeValueList">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="proposal">
+ <xsd:annotation>
+ <xsd:documentation>Represents a translation proposal from a translation memory or other resource.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="previous-version">
+ <xsd:annotation>
+ <xsd:documentation>Represents a previous version of the target element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="rejected">
+ <xsd:annotation>
+ <xsd:documentation>Represents a rejected version of the target element.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="reference">
+ <xsd:annotation>
+ <xsd:documentation>Represents a translation to be used for reference purposes only, for example from a related product or a different language.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ <xsd:enumeration value="accepted">
+ <xsd:annotation>
+ <xsd:documentation>Represents a proposed translation that was used for the translation of the trans-unit, possibly modified.</xsd:documentation>
+ </xsd:annotation>
+ </xsd:enumeration>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <!-- Other Types -->
+ <xsd:complexType name="ElemType_ExternalReference">
+ <xsd:choice>
+ <xsd:element ref="xlf:internal-file"/>
+ <xsd:element ref="xlf:external-file"/>
+ </xsd:choice>
+ </xsd:complexType>
+ <xsd:simpleType name="AttrType_purpose">
+ <xsd:list>
+ <xsd:simpleType>
+ <xsd:union memberTypes="xlf:purposeValueList xlf:XTend"/>
+ </xsd:simpleType>
+ </xsd:list>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_datatype">
+ <xsd:union memberTypes="xlf:datatypeValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_restype">
+ <xsd:union memberTypes="xlf:restypeValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_alttranstype">
+ <xsd:union memberTypes="xlf:alttranstypeValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_context-type">
+ <xsd:union memberTypes="xlf:context-typeValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_state">
+ <xsd:union memberTypes="xlf:stateValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_state-qualifier">
+ <xsd:union memberTypes="xlf:state-qualifierValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_count-type">
+ <xsd:union memberTypes="xlf:restypeValueList xlf:count-typeValueList xlf:datatypeValueList xlf:stateValueList xlf:state-qualifierValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_InlineDelimiters">
+ <xsd:union memberTypes="xlf:InlineDelimitersValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_InlinePlaceholders">
+ <xsd:union memberTypes="xlf:InlinePlaceholdersValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_size-unit">
+ <xsd:union memberTypes="xlf:size-unitValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_mtype">
+ <xsd:union memberTypes="xlf:mtypeValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_unit">
+ <xsd:union memberTypes="xlf:unitValueList xlf:XTend"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_priority">
+ <xsd:union memberTypes="xlf:priorityValueList"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_reformat">
+ <xsd:union memberTypes="xlf:reformatValueYesNo xlf:reformatValueList"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_YesNo">
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="yes"/>
+ <xsd:enumeration value="no"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_Position">
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="open"/>
+ <xsd:enumeration value="close"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_assoc">
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="preceding"/>
+ <xsd:enumeration value="following"/>
+ <xsd:enumeration value="both"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_annotates">
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="source"/>
+ <xsd:enumeration value="target"/>
+ <xsd:enumeration value="general"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_Coordinates">
+ <xsd:annotation>
+ <xsd:documentation>Values for the attribute 'coord'.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="(-?\d+|#);(-?\d+|#);(-?\d+|#);(-?\d+|#)"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="AttrType_Version">
+ <xsd:annotation>
+ <xsd:documentation>Version values: 1.0 and 1.1 are allowed for backward compatibility.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="1.2"/>
+ <xsd:enumeration value="1.1"/>
+ <xsd:enumeration value="1.0"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <!-- Groups -->
+ <xsd:group name="ElemGroup_TextContent">
+ <xsd:choice>
+ <xsd:element ref="xlf:g"/>
+ <xsd:element ref="xlf:bpt"/>
+ <xsd:element ref="xlf:ept"/>
+ <xsd:element ref="xlf:ph"/>
+ <xsd:element ref="xlf:it"/>
+ <xsd:element ref="xlf:mrk"/>
+ <xsd:element ref="xlf:x"/>
+ <xsd:element ref="xlf:bx"/>
+ <xsd:element ref="xlf:ex"/>
+ </xsd:choice>
+ </xsd:group>
+ <xsd:attributeGroup name="AttrGroup_TextContent">
+ <xsd:attribute name="id" type="xsd:string" use="required"/>
+ <xsd:attribute name="xid" type="xsd:string" use="optional"/>
+ <xsd:attribute name="equiv-text" type="xsd:string" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:attributeGroup>
+ <!-- XLIFF Structure -->
+ <xsd:element name="xliff">
+ <xsd:complexType>
+ <xsd:sequence maxOccurs="unbounded">
+ <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+ <xsd:element ref="xlf:file"/>
+ </xsd:sequence>
+ <xsd:attribute name="version" type="xlf:AttrType_Version" use="required"/>
+ <xsd:attribute ref="xml:lang" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="file">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element minOccurs="0" ref="xlf:header"/>
+ <xsd:element ref="xlf:body"/>
+ </xsd:sequence>
+ <xsd:attribute name="original" type="xsd:string" use="required"/>
+ <xsd:attribute name="source-language" type="xsd:language" use="required"/>
+ <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="required"/>
+ <xsd:attribute name="tool-id" type="xsd:string" use="optional"/>
+ <xsd:attribute name="date" type="xsd:dateTime" use="optional"/>
+ <xsd:attribute ref="xml:space" use="optional"/>
+ <xsd:attribute name="category" type="xsd:string" use="optional"/>
+ <xsd:attribute name="target-language" type="xsd:language" use="optional"/>
+ <xsd:attribute name="product-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="product-version" type="xsd:string" use="optional"/>
+ <xsd:attribute name="build-num" type="xsd:string" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ <xsd:unique name="U_group_id">
+ <xsd:selector xpath=".//xlf:group"/>
+ <xsd:field xpath="@id"/>
+ </xsd:unique>
+ <xsd:key name="K_unit_id">
+ <xsd:selector xpath=".//xlf:trans-unit|.//xlf:bin-unit"/>
+ <xsd:field xpath="@id"/>
+ </xsd:key>
+ <xsd:keyref name="KR_unit_id" refer="xlf:K_unit_id">
+ <xsd:selector xpath=".//bpt|.//ept|.//it|.//ph|.//g|.//x|.//bx|.//ex|.//sub"/>
+ <xsd:field xpath="@xid"/>
+ </xsd:keyref>
+ <xsd:key name="K_tool-id">
+ <xsd:selector xpath="xlf:header/xlf:tool"/>
+ <xsd:field xpath="@tool-id"/>
+ </xsd:key>
+ <xsd:keyref name="KR_file_tool-id" refer="xlf:K_tool-id">
+ <xsd:selector xpath="."/>
+ <xsd:field xpath="@tool-id"/>
+ </xsd:keyref>
+ <xsd:keyref name="KR_phase_tool-id" refer="xlf:K_tool-id">
+ <xsd:selector xpath="xlf:header/xlf:phase-group/xlf:phase"/>
+ <xsd:field xpath="@tool-id"/>
+ </xsd:keyref>
+ <xsd:keyref name="KR_alt-trans_tool-id" refer="xlf:K_tool-id">
+ <xsd:selector xpath=".//xlf:trans-unit/xlf:alt-trans"/>
+ <xsd:field xpath="@tool-id"/>
+ </xsd:keyref>
+ <xsd:key name="K_count-group_name">
+ <xsd:selector xpath=".//xlf:count-group"/>
+ <xsd:field xpath="@name"/>
+ </xsd:key>
+ <xsd:unique name="U_context-group_name">
+ <xsd:selector xpath=".//xlf:context-group"/>
+ <xsd:field xpath="@name"/>
+ </xsd:unique>
+ <xsd:key name="K_phase-name">
+ <xsd:selector xpath="xlf:header/xlf:phase-group/xlf:phase"/>
+ <xsd:field xpath="@phase-name"/>
+ </xsd:key>
+ <xsd:keyref name="KR_phase-name" refer="xlf:K_phase-name">
+ <xsd:selector xpath=".//xlf:count|.//xlf:trans-unit|.//xlf:target|.//bin-unit|.//bin-target"/>
+ <xsd:field xpath="@phase-name"/>
+ </xsd:keyref>
+ <xsd:unique name="U_uid">
+ <xsd:selector xpath=".//xlf:external-file"/>
+ <xsd:field xpath="@uid"/>
+ </xsd:unique>
+ </xsd:element>
+ <xsd:element name="header">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element minOccurs="0" name="skl" type="xlf:ElemType_ExternalReference"/>
+ <xsd:element minOccurs="0" ref="xlf:phase-group"/>
+ <xsd:choice maxOccurs="unbounded" minOccurs="0">
+ <xsd:element name="glossary" type="xlf:ElemType_ExternalReference"/>
+ <xsd:element name="reference" type="xlf:ElemType_ExternalReference"/>
+ <xsd:element ref="xlf:count-group"/>
+ <xsd:element ref="xlf:note"/>
+ <xsd:element ref="xlf:tool"/>
+ </xsd:choice>
+ <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="internal-file">
+ <xsd:complexType>
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="form" type="xsd:string"/>
+ <xsd:attribute name="crc" type="xsd:NMTOKEN"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="external-file">
+ <xsd:complexType>
+ <xsd:attribute name="href" type="xsd:string" use="required"/>
+ <xsd:attribute name="crc" type="xsd:NMTOKEN"/>
+ <xsd:attribute name="uid" type="xsd:NMTOKEN"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="note">
+ <xsd:complexType>
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute ref="xml:lang" use="optional"/>
+ <xsd:attribute default="1" name="priority" type="xlf:AttrType_priority" use="optional"/>
+ <xsd:attribute name="from" type="xsd:string" use="optional"/>
+ <xsd:attribute default="general" name="annotates" type="xlf:AttrType_annotates" use="optional"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="phase-group">
+ <xsd:complexType>
+ <xsd:sequence maxOccurs="unbounded">
+ <xsd:element ref="xlf:phase"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="phase">
+ <xsd:complexType>
+ <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:note"/>
+ </xsd:sequence>
+ <xsd:attribute name="phase-name" type="xsd:string" use="required"/>
+ <xsd:attribute name="process-name" type="xsd:string" use="required"/>
+ <xsd:attribute name="company-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="tool-id" type="xsd:string" use="optional"/>
+ <xsd:attribute name="date" type="xsd:dateTime" use="optional"/>
+ <xsd:attribute name="job-id" type="xsd:string" use="optional"/>
+ <xsd:attribute name="contact-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="contact-email" type="xsd:string" use="optional"/>
+ <xsd:attribute name="contact-phone" type="xsd:string" use="optional"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="count-group">
+ <xsd:complexType>
+ <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:count"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="count">
+ <xsd:complexType>
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="count-type" type="xlf:AttrType_count-type" use="optional"/>
+ <xsd:attribute name="phase-name" type="xsd:string" use="optional"/>
+ <xsd:attribute default="word" name="unit" type="xlf:AttrType_unit" use="optional"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="context-group">
+ <xsd:complexType>
+ <xsd:sequence maxOccurs="unbounded">
+ <xsd:element ref="xlf:context"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="purpose" type="xlf:AttrType_purpose" use="optional"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="context">
+ <xsd:complexType>
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="context-type" type="xlf:AttrType_context-type" use="required"/>
+ <xsd:attribute default="no" name="match-mandatory" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="tool">
+ <xsd:complexType mixed="true">
+ <xsd:sequence>
+ <xsd:any namespace="##any" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="tool-id" type="xsd:string" use="required"/>
+ <xsd:attribute name="tool-name" type="xsd:string" use="required"/>
+ <xsd:attribute name="tool-version" type="xsd:string" use="optional"/>
+ <xsd:attribute name="tool-company" type="xsd:string" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="body">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded" minOccurs="0">
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:group"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:trans-unit"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:bin-unit"/>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="group">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:sequence>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:context-group"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:count-group"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:note"/>
+ <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+ </xsd:sequence>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:group"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:trans-unit"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:bin-unit"/>
+ </xsd:choice>
+ </xsd:sequence>
+ <xsd:attribute name="id" type="xsd:string" use="optional"/>
+ <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+ <xsd:attribute default="default" ref="xml:space" use="optional"/>
+ <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+ <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+ <xsd:attribute name="extradata" type="xsd:string" use="optional"/>
+ <xsd:attribute name="extype" type="xsd:string" use="optional"/>
+ <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="menu" type="xsd:string" use="optional"/>
+ <xsd:attribute name="menu-option" type="xsd:string" use="optional"/>
+ <xsd:attribute name="menu-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+ <xsd:attribute name="font" type="xsd:string" use="optional"/>
+ <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+ <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/>
+ <xsd:attribute default="pixel" name="size-unit" type="xlf:AttrType_size-unit" use="optional"/>
+ <xsd:attribute name="maxwidth" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="minwidth" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="maxheight" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="minheight" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="maxbytes" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="minbytes" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="charclass" type="xsd:string" use="optional"/>
+ <xsd:attribute default="no" name="merged-trans" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="trans-unit">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element ref="xlf:source"/>
+ <xsd:element minOccurs="0" ref="xlf:seg-source"/>
+ <xsd:element minOccurs="0" ref="xlf:target"/>
+ <xsd:choice maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:context-group"/>
+ <xsd:element ref="xlf:count-group"/>
+ <xsd:element ref="xlf:note"/>
+ <xsd:element ref="xlf:alt-trans"/>
+ </xsd:choice>
+ <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+ </xsd:sequence>
+ <xsd:attribute name="id" type="xsd:string" use="required"/>
+ <xsd:attribute name="approved" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/>
+ <xsd:attribute default="default" ref="xml:space" use="optional"/>
+ <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+ <xsd:attribute name="phase-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+ <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+ <xsd:attribute name="extradata" type="xsd:string" use="optional"/>
+ <xsd:attribute name="extype" type="xsd:string" use="optional"/>
+ <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="menu" type="xsd:string" use="optional"/>
+ <xsd:attribute name="menu-option" type="xsd:string" use="optional"/>
+ <xsd:attribute name="menu-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+ <xsd:attribute name="font" type="xsd:string" use="optional"/>
+ <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+ <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute default="pixel" name="size-unit" type="xlf:AttrType_size-unit" use="optional"/>
+ <xsd:attribute name="maxwidth" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="minwidth" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="maxheight" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="minheight" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="maxbytes" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="minbytes" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="charclass" type="xsd:string" use="optional"/>
+ <xsd:attribute default="yes" name="merged-trans" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ <xsd:unique name="U_tu_segsrc_mid">
+ <xsd:selector xpath="./xlf:seg-source/xlf:mrk"/>
+ <xsd:field xpath="@mid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_tu_segsrc_mid" refer="xlf:U_tu_segsrc_mid">
+ <xsd:selector xpath="./xlf:target/xlf:mrk|./xlf:alt-trans"/>
+ <xsd:field xpath="@mid"/>
+ </xsd:keyref>
+ </xsd:element>
+ <xsd:element name="source">
+ <xsd:complexType mixed="true">
+ <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+ <xsd:attribute ref="xml:lang" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ <xsd:unique name="U_source_bpt_rid">
+ <xsd:selector xpath=".//xlf:bpt"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_source_ept_rid" refer="xlf:U_source_bpt_rid">
+ <xsd:selector xpath=".//xlf:ept"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:keyref>
+ <xsd:unique name="U_source_bx_rid">
+ <xsd:selector xpath=".//xlf:bx"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_source_ex_rid" refer="xlf:U_source_bx_rid">
+ <xsd:selector xpath=".//xlf:ex"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:keyref>
+ </xsd:element>
+ <xsd:element name="seg-source">
+ <xsd:complexType mixed="true">
+ <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+ <xsd:attribute ref="xml:lang" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ <xsd:unique name="U_segsrc_bpt_rid">
+ <xsd:selector xpath=".//xlf:bpt"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_segsrc_ept_rid" refer="xlf:U_segsrc_bpt_rid">
+ <xsd:selector xpath=".//xlf:ept"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:keyref>
+ <xsd:unique name="U_segsrc_bx_rid">
+ <xsd:selector xpath=".//xlf:bx"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_segsrc_ex_rid" refer="xlf:U_segsrc_bx_rid">
+ <xsd:selector xpath=".//xlf:ex"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:keyref>
+ </xsd:element>
+ <xsd:element name="target">
+ <xsd:complexType mixed="true">
+ <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+ <xsd:attribute name="state" type="xlf:AttrType_state" use="optional"/>
+ <xsd:attribute name="state-qualifier" type="xlf:AttrType_state-qualifier" use="optional"/>
+ <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute ref="xml:lang" use="optional"/>
+ <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+ <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+ <xsd:attribute name="font" type="xsd:string" use="optional"/>
+ <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+ <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute default="yes" name="equiv-trans" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ <xsd:unique name="U_target_bpt_rid">
+ <xsd:selector xpath=".//xlf:bpt"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_target_ept_rid" refer="xlf:U_target_bpt_rid">
+ <xsd:selector xpath=".//xlf:ept"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:keyref>
+ <xsd:unique name="U_target_bx_rid">
+ <xsd:selector xpath=".//xlf:bx"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_target_ex_rid" refer="xlf:U_target_bx_rid">
+ <xsd:selector xpath=".//xlf:ex"/>
+ <xsd:field xpath="@rid"/>
+ </xsd:keyref>
+ </xsd:element>
+ <xsd:element name="alt-trans">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element minOccurs="0" ref="xlf:source"/>
+ <xsd:element minOccurs="0" ref="xlf:seg-source"/>
+ <xsd:element maxOccurs="1" ref="xlf:target"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:context-group"/>
+ <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:note"/>
+ <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+ </xsd:sequence>
+ <xsd:attribute name="match-quality" type="xsd:string" use="optional"/>
+ <xsd:attribute name="tool-id" type="xsd:string" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute ref="xml:lang" use="optional"/>
+ <xsd:attribute name="origin" type="xsd:string" use="optional"/>
+ <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+ <xsd:attribute default="default" ref="xml:space" use="optional"/>
+ <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+ <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+ <xsd:attribute name="extradata" type="xsd:string" use="optional"/>
+ <xsd:attribute name="extype" type="xsd:string" use="optional"/>
+ <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="menu" type="xsd:string" use="optional"/>
+ <xsd:attribute name="menu-option" type="xsd:string" use="optional"/>
+ <xsd:attribute name="menu-name" type="xsd:string" use="optional"/>
+ <xsd:attribute name="mid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+ <xsd:attribute name="font" type="xsd:string" use="optional"/>
+ <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+ <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute default="proposal" name="alttranstype" type="xlf:AttrType_alttranstype" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ <xsd:unique name="U_at_segsrc_mid">
+ <xsd:selector xpath="./xlf:seg-source/xlf:mrk"/>
+ <xsd:field xpath="@mid"/>
+ </xsd:unique>
+ <xsd:keyref name="KR_at_segsrc_mid" refer="xlf:U_at_segsrc_mid">
+ <xsd:selector xpath="./xlf:target/xlf:mrk"/>
+ <xsd:field xpath="@mid"/>
+ </xsd:keyref>
+ </xsd:element>
+ <xsd:element name="bin-unit">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element ref="xlf:bin-source"/>
+ <xsd:element minOccurs="0" ref="xlf:bin-target"/>
+ <xsd:choice maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:context-group"/>
+ <xsd:element ref="xlf:count-group"/>
+ <xsd:element ref="xlf:note"/>
+ <xsd:element ref="xlf:trans-unit"/>
+ </xsd:choice>
+ <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+ </xsd:sequence>
+ <xsd:attribute name="id" type="xsd:string" use="required"/>
+ <xsd:attribute name="mime-type" type="xlf:mime-typeValueList" use="required"/>
+ <xsd:attribute name="approved" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/>
+ <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+ <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+ <xsd:attribute name="phase-name" type="xsd:string" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="bin-source">
+ <xsd:complexType>
+ <xsd:choice>
+ <xsd:element ref="xlf:internal-file"/>
+ <xsd:element ref="xlf:external-file"/>
+ </xsd:choice>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="bin-target">
+ <xsd:complexType>
+ <xsd:choice>
+ <xsd:element ref="xlf:internal-file"/>
+ <xsd:element ref="xlf:external-file"/>
+ </xsd:choice>
+ <xsd:attribute name="mime-type" type="xlf:mime-typeValueList" use="optional"/>
+ <xsd:attribute name="state" type="xlf:AttrType_state" use="optional"/>
+ <xsd:attribute name="state-qualifier" type="xlf:AttrType_state-qualifier" use="optional"/>
+ <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+ <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+ <!-- Element for inline codes -->
+ <xsd:element name="g">
+ <xsd:complexType mixed="true">
+ <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+ <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="x">
+ <xsd:complexType>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlinePlaceholders" use="optional"/>
+ <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="bx">
+ <xsd:complexType>
+ <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+ <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="ex">
+ <xsd:complexType>
+ <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="ph">
+ <xsd:complexType mixed="true">
+ <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:sub"/>
+ </xsd:sequence>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlinePlaceholders" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+ <xsd:attribute name="assoc" type="xlf:AttrType_assoc" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="bpt">
+ <xsd:complexType mixed="true">
+ <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:sub"/>
+ </xsd:sequence>
+ <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="ept">
+ <xsd:complexType mixed="true">
+ <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:sub"/>
+ </xsd:sequence>
+ <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="it">
+ <xsd:complexType mixed="true">
+ <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+ <xsd:element ref="xlf:sub"/>
+ </xsd:sequence>
+ <xsd:attribute name="pos" type="xlf:AttrType_Position" use="required"/>
+ <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+ <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+ <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="sub">
+ <xsd:complexType mixed="true">
+ <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+ <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+ <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+ <xsd:attribute name="xid" type="xsd:string" use="optional"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="mrk">
+ <xsd:complexType mixed="true">
+ <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+ <xsd:attribute name="mtype" type="xlf:AttrType_mtype" use="required"/>
+ <xsd:attribute name="mid" type="xsd:NMTOKEN" use="optional"/>
+ <xsd:attribute name="comment" type="xsd:string" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="strict"/>
+ </xsd:complexType>
+ </xsd:element>
+</xsd:schema>
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Catalogue;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+abstract class AbstractOperationTest extends TestCase
+{
+ public function testGetEmptyDomains()
+ {
+ $this->assertEquals(
+ array(),
+ $this->createOperation(
+ new MessageCatalogue('en'),
+ new MessageCatalogue('en')
+ )->getDomains()
+ );
+ }
+
+ public function testGetMergedDomains()
+ {
+ $this->assertEquals(
+ array('a', 'b', 'c'),
+ $this->createOperation(
+ new MessageCatalogue('en', array('a' => array(), 'b' => array())),
+ new MessageCatalogue('en', array('b' => array(), 'c' => array()))
+ )->getDomains()
+ );
+ }
+
+ public function testGetMessagesFromUnknownDomain()
+ {
+ $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
+ $this->createOperation(
+ new MessageCatalogue('en'),
+ new MessageCatalogue('en')
+ )->getMessages('domain');
+ }
+
+ public function testGetEmptyMessages()
+ {
+ $this->assertEquals(
+ array(),
+ $this->createOperation(
+ new MessageCatalogue('en', array('a' => array())),
+ new MessageCatalogue('en')
+ )->getMessages('a')
+ );
+ }
+
+ public function testGetEmptyResult()
+ {
+ $this->assertEquals(
+ new MessageCatalogue('en'),
+ $this->createOperation(
+ new MessageCatalogue('en'),
+ new MessageCatalogue('en')
+ )->getResult()
+ );
+ }
+
+ abstract protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target);
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Catalogue;
+
+use Symfony\Component\Translation\Catalogue\MergeOperation;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+class MergeOperationTest extends AbstractOperationTest
+{
+ public function testGetMessagesFromSingleDomain()
+ {
+ $operation = $this->createOperation(
+ new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))),
+ new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c')))
+ );
+
+ $this->assertEquals(
+ array('a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'),
+ $operation->getMessages('messages')
+ );
+
+ $this->assertEquals(
+ array('c' => 'new_c'),
+ $operation->getNewMessages('messages')
+ );
+
+ $this->assertEquals(
+ array(),
+ $operation->getObsoleteMessages('messages')
+ );
+ }
+
+ public function testGetResultFromSingleDomain()
+ {
+ $this->assertEquals(
+ new MessageCatalogue('en', array(
+ 'messages' => array('a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'),
+ )),
+ $this->createOperation(
+ new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))),
+ new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c')))
+ )->getResult()
+ );
+ }
+
+ public function testGetResultWithMetadata()
+ {
+ $leftCatalogue = new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b')));
+ $leftCatalogue->setMetadata('a', 'foo', 'messages');
+ $leftCatalogue->setMetadata('b', 'bar', 'messages');
+ $rightCatalogue = new MessageCatalogue('en', array('messages' => array('b' => 'new_b', 'c' => 'new_c')));
+ $rightCatalogue->setMetadata('b', 'baz', 'messages');
+ $rightCatalogue->setMetadata('c', 'qux', 'messages');
+
+ $mergedCatalogue = new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c')));
+ $mergedCatalogue->setMetadata('a', 'foo', 'messages');
+ $mergedCatalogue->setMetadata('b', 'bar', 'messages');
+ $mergedCatalogue->setMetadata('c', 'qux', 'messages');
+
+ $this->assertEquals(
+ $mergedCatalogue,
+ $this->createOperation(
+ $leftCatalogue,
+ $rightCatalogue
+ )->getResult()
+ );
+ }
+
+ protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
+ {
+ return new MergeOperation($source, $target);
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Catalogue;
+
+use Symfony\Component\Translation\Catalogue\TargetOperation;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+class TargetOperationTest extends AbstractOperationTest
+{
+ public function testGetMessagesFromSingleDomain()
+ {
+ $operation = $this->createOperation(
+ new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))),
+ new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c')))
+ );
+
+ $this->assertEquals(
+ array('a' => 'old_a', 'c' => 'new_c'),
+ $operation->getMessages('messages')
+ );
+
+ $this->assertEquals(
+ array('c' => 'new_c'),
+ $operation->getNewMessages('messages')
+ );
+
+ $this->assertEquals(
+ array('b' => 'old_b'),
+ $operation->getObsoleteMessages('messages')
+ );
+ }
+
+ public function testGetResultFromSingleDomain()
+ {
+ $this->assertEquals(
+ new MessageCatalogue('en', array(
+ 'messages' => array('a' => 'old_a', 'c' => 'new_c'),
+ )),
+ $this->createOperation(
+ new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))),
+ new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c')))
+ )->getResult()
+ );
+ }
+
+ public function testGetResultWithMetadata()
+ {
+ $leftCatalogue = new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b')));
+ $leftCatalogue->setMetadata('a', 'foo', 'messages');
+ $leftCatalogue->setMetadata('b', 'bar', 'messages');
+ $rightCatalogue = new MessageCatalogue('en', array('messages' => array('b' => 'new_b', 'c' => 'new_c')));
+ $rightCatalogue->setMetadata('b', 'baz', 'messages');
+ $rightCatalogue->setMetadata('c', 'qux', 'messages');
+
+ $diffCatalogue = new MessageCatalogue('en', array('messages' => array('b' => 'old_b', 'c' => 'new_c')));
+ $diffCatalogue->setMetadata('b', 'bar', 'messages');
+ $diffCatalogue->setMetadata('c', 'qux', 'messages');
+
+ $this->assertEquals(
+ $diffCatalogue,
+ $this->createOperation(
+ $leftCatalogue,
+ $rightCatalogue
+ )->getResult()
+ );
+ }
+
+ protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
+ {
+ return new TargetOperation($source, $target);
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\DataCollector;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\DataCollectorTranslator;
+use Symfony\Component\Translation\DataCollector\TranslationDataCollector;
+
+class TranslationDataCollectorTest extends TestCase
+{
+ protected function setUp()
+ {
+ if (!class_exists('Symfony\Component\HttpKernel\DataCollector\DataCollector')) {
+ $this->markTestSkipped('The "DataCollector" is not available');
+ }
+ }
+
+ public function testCollectEmptyMessages()
+ {
+ $translator = $this->getTranslator();
+ $translator->expects($this->any())->method('getCollectedMessages')->will($this->returnValue(array()));
+
+ $dataCollector = new TranslationDataCollector($translator);
+ $dataCollector->lateCollect();
+
+ $this->assertEquals(0, $dataCollector->getCountMissings());
+ $this->assertEquals(0, $dataCollector->getCountFallbacks());
+ $this->assertEquals(0, $dataCollector->getCountDefines());
+ $this->assertEquals(array(), $dataCollector->getMessages()->getValue());
+ }
+
+ public function testCollect()
+ {
+ $collectedMessages = array(
+ array(
+ 'id' => 'foo',
+ 'translation' => 'foo (en)',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_DEFINED,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ ),
+ array(
+ 'id' => 'bar',
+ 'translation' => 'bar (fr)',
+ 'locale' => 'fr',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ ),
+ array(
+ 'id' => 'choice',
+ 'translation' => 'choice',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_MISSING,
+ 'parameters' => array('%count%' => 3),
+ 'transChoiceNumber' => 3,
+ ),
+ array(
+ 'id' => 'choice',
+ 'translation' => 'choice',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_MISSING,
+ 'parameters' => array('%count%' => 3),
+ 'transChoiceNumber' => 3,
+ ),
+ array(
+ 'id' => 'choice',
+ 'translation' => 'choice',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_MISSING,
+ 'parameters' => array('%count%' => 4, '%foo%' => 'bar'),
+ 'transChoiceNumber' => 4,
+ ),
+ );
+ $expectedMessages = array(
+ array(
+ 'id' => 'foo',
+ 'translation' => 'foo (en)',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_DEFINED,
+ 'count' => 1,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ ),
+ array(
+ 'id' => 'bar',
+ 'translation' => 'bar (fr)',
+ 'locale' => 'fr',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+ 'count' => 1,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ ),
+ array(
+ 'id' => 'choice',
+ 'translation' => 'choice',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_MISSING,
+ 'count' => 3,
+ 'parameters' => array(
+ array('%count%' => 3),
+ array('%count%' => 3),
+ array('%count%' => 4, '%foo%' => 'bar'),
+ ),
+ 'transChoiceNumber' => 3,
+ ),
+ );
+
+ $translator = $this->getTranslator();
+ $translator->expects($this->any())->method('getCollectedMessages')->will($this->returnValue($collectedMessages));
+
+ $dataCollector = new TranslationDataCollector($translator);
+ $dataCollector->lateCollect();
+
+ $this->assertEquals(1, $dataCollector->getCountMissings());
+ $this->assertEquals(1, $dataCollector->getCountFallbacks());
+ $this->assertEquals(1, $dataCollector->getCountDefines());
+
+ $this->assertEquals($expectedMessages, array_values($dataCollector->getMessages()->getValue(true)));
+ }
+
+ private function getTranslator()
+ {
+ $translator = $this
+ ->getMockBuilder('Symfony\Component\Translation\DataCollectorTranslator')
+ ->disableOriginalConstructor()
+ ->getMock()
+ ;
+
+ return $translator;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Translator;
+use Symfony\Component\Translation\DataCollectorTranslator;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+
+class DataCollectorTranslatorTest extends TestCase
+{
+ public function testCollectMessages()
+ {
+ $collector = $this->createCollector();
+ $collector->setFallbackLocales(array('fr', 'ru'));
+
+ $collector->trans('foo');
+ $collector->trans('bar');
+ $collector->transChoice('choice', 0);
+ $collector->trans('bar_ru');
+ $collector->trans('bar_ru', array('foo' => 'bar'));
+
+ $expectedMessages = array();
+ $expectedMessages[] = array(
+ 'id' => 'foo',
+ 'translation' => 'foo (en)',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_DEFINED,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ );
+ $expectedMessages[] = array(
+ 'id' => 'bar',
+ 'translation' => 'bar (fr)',
+ 'locale' => 'fr',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ );
+ $expectedMessages[] = array(
+ 'id' => 'choice',
+ 'translation' => 'choice',
+ 'locale' => 'en',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_MISSING,
+ 'parameters' => array(),
+ 'transChoiceNumber' => 0,
+ );
+ $expectedMessages[] = array(
+ 'id' => 'bar_ru',
+ 'translation' => 'bar (ru)',
+ 'locale' => 'ru',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+ 'parameters' => array(),
+ 'transChoiceNumber' => null,
+ );
+ $expectedMessages[] = array(
+ 'id' => 'bar_ru',
+ 'translation' => 'bar (ru)',
+ 'locale' => 'ru',
+ 'domain' => 'messages',
+ 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+ 'parameters' => array('foo' => 'bar'),
+ 'transChoiceNumber' => null,
+ );
+
+ $this->assertEquals($expectedMessages, $collector->getCollectedMessages());
+ }
+
+ private function createCollector()
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (en)'), 'en');
+ $translator->addResource('array', array('bar' => 'bar (fr)'), 'fr');
+ $translator->addResource('array', array('bar_ru' => 'bar (ru)'), 'ru');
+
+ return new DataCollectorTranslator($translator);
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\CsvFileDumper;
+
+class CsvFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar', 'bar' => 'foo
+foo', 'foo;foo' => 'bar'));
+
+ $dumper = new CsvFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/valid.csv', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\FileDumper;
+
+class FileDumperTest extends TestCase
+{
+ public function testDump()
+ {
+ $tempDir = sys_get_temp_dir();
+
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new ConcreteFileDumper();
+ $dumper->dump($catalogue, array('path' => $tempDir));
+
+ $this->assertFileExists($tempDir.'/messages.en.concrete');
+ }
+
+ /**
+ * @group legacy
+ */
+ public function testDumpBackupsFileIfExisting()
+ {
+ $tempDir = sys_get_temp_dir();
+ $file = $tempDir.'/messages.en.concrete';
+ $backupFile = $file.'~';
+
+ @touch($file);
+
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new ConcreteFileDumper();
+ $dumper->dump($catalogue, array('path' => $tempDir));
+
+ $this->assertFileExists($backupFile);
+
+ @unlink($file);
+ @unlink($backupFile);
+ }
+
+ public function testDumpCreatesNestedDirectoriesAndFile()
+ {
+ $tempDir = sys_get_temp_dir();
+ $translationsDir = $tempDir.'/test/translations';
+ $file = $translationsDir.'/messages.en.concrete';
+
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new ConcreteFileDumper();
+ $dumper->setRelativePathTemplate('test/translations/%domain%.%locale%.%extension%');
+ $dumper->dump($catalogue, array('path' => $tempDir));
+
+ $this->assertFileExists($file);
+
+ @unlink($file);
+ @rmdir($translationsDir);
+ }
+}
+
+class ConcreteFileDumper extends FileDumper
+{
+ public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
+ {
+ return '';
+ }
+
+ protected function getExtension()
+ {
+ return 'concrete';
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\IcuResFileDumper;
+
+class IcuResFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new IcuResFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resourcebundle/res/en.res', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\IniFileDumper;
+
+class IniFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new IniFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ini', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\JsonFileDumper;
+
+class JsonFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new JsonFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.json', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+
+ public function testDumpWithCustomEncoding()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => '"bar"'));
+
+ $dumper = new JsonFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.dump.json', $dumper->formatCatalogue($catalogue, 'messages', array('json_encoding' => JSON_HEX_QUOT)));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\MoFileDumper;
+
+class MoFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new MoFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.mo', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\PhpFileDumper;
+
+class PhpFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new PhpFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.php', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\PoFileDumper;
+
+class PoFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new PoFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.po', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\QtFileDumper;
+
+class QtFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(array('foo' => 'bar'), 'resources');
+
+ $dumper = new QtFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ts', $dumper->formatCatalogue($catalogue, 'resources'));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\XliffFileDumper;
+
+class XliffFileDumperTest extends TestCase
+{
+ public function testFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en_US');
+ $catalogue->add(array(
+ 'foo' => 'bar',
+ 'key' => '',
+ 'key.with.cdata' => '<source> & <target>',
+ ));
+ $catalogue->setMetadata('foo', array('notes' => array(array('priority' => 1, 'from' => 'bar', 'content' => 'baz'))));
+ $catalogue->setMetadata('key', array('notes' => array(array('content' => 'baz'), array('content' => 'qux'))));
+
+ $dumper = new XliffFileDumper();
+
+ $this->assertStringEqualsFile(
+ __DIR__.'/../fixtures/resources-clean.xlf',
+ $dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR'))
+ );
+ }
+
+ public function testFormatCatalogueXliff2()
+ {
+ $catalogue = new MessageCatalogue('en_US');
+ $catalogue->add(array(
+ 'foo' => 'bar',
+ 'key' => '',
+ 'key.with.cdata' => '<source> & <target>',
+ ));
+ $catalogue->setMetadata('key', array('target-attributes' => array('order' => 1)));
+
+ $dumper = new XliffFileDumper();
+
+ $this->assertStringEqualsFile(
+ __DIR__.'/../fixtures/resources-2.0-clean.xlf',
+ $dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR', 'xliff_version' => '2.0'))
+ );
+ }
+
+ public function testFormatCatalogueWithCustomToolInfo()
+ {
+ $options = array(
+ 'default_locale' => 'en_US',
+ 'tool_info' => array('tool-id' => 'foo', 'tool-name' => 'foo', 'tool-version' => '0.0', 'tool-company' => 'Foo'),
+ );
+
+ $catalogue = new MessageCatalogue('en_US');
+ $catalogue->add(array('foo' => 'bar'));
+
+ $dumper = new XliffFileDumper();
+
+ $this->assertStringEqualsFile(
+ __DIR__.'/../fixtures/resources-tool-info.xlf',
+ $dumper->formatCatalogue($catalogue, 'messages', $options)
+ );
+ }
+
+ public function testFormatCatalogueWithTargetAttributesMetadata()
+ {
+ $catalogue = new MessageCatalogue('en_US');
+ $catalogue->add(array(
+ 'foo' => 'bar',
+ ));
+ $catalogue->setMetadata('foo', array('target-attributes' => array('state' => 'needs-translation')));
+
+ $dumper = new XliffFileDumper();
+
+ $this->assertStringEqualsFile(
+ __DIR__.'/../fixtures/resources-target-attributes.xlf',
+ $dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR'))
+ );
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\YamlFileDumper;
+
+class YamlFileDumperTest extends TestCase
+{
+ public function testTreeFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(
+ array(
+ 'foo.bar1' => 'value1',
+ 'foo.bar2' => 'value2',
+ ));
+
+ $dumper = new YamlFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/messages.yml', $dumper->formatCatalogue($catalogue, 'messages', array('as_tree' => true, 'inline' => 999)));
+ }
+
+ public function testLinearFormatCatalogue()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->add(
+ array(
+ 'foo.bar1' => 'value1',
+ 'foo.bar2' => 'value2',
+ ));
+
+ $dumper = new YamlFileDumper();
+
+ $this->assertStringEqualsFile(__DIR__.'/../fixtures/messages_linear.yml', $dumper->formatCatalogue($catalogue, 'messages'));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Intl\Util\IntlTestHelper;
+use Symfony\Component\Translation\IdentityTranslator;
+
+class IdentityTranslatorTest extends TestCase
+{
+ /**
+ * @dataProvider getTransTests
+ */
+ public function testTrans($expected, $id, $parameters)
+ {
+ $translator = new IdentityTranslator();
+
+ $this->assertEquals($expected, $translator->trans($id, $parameters));
+ }
+
+ /**
+ * @dataProvider getTransChoiceTests
+ */
+ public function testTransChoiceWithExplicitLocale($expected, $id, $number, $parameters)
+ {
+ $translator = new IdentityTranslator();
+ $translator->setLocale('en');
+
+ $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters));
+ }
+
+ /**
+ * @dataProvider getTransChoiceTests
+ */
+ public function testTransChoiceWithDefaultLocale($expected, $id, $number, $parameters)
+ {
+ \Locale::setDefault('en');
+
+ $translator = new IdentityTranslator();
+
+ $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters));
+ }
+
+ public function testGetSetLocale()
+ {
+ $translator = new IdentityTranslator();
+ $translator->setLocale('en');
+
+ $this->assertEquals('en', $translator->getLocale());
+ }
+
+ public function testGetLocaleReturnsDefaultLocaleIfNotSet()
+ {
+ // in order to test with "pt_BR"
+ IntlTestHelper::requireFullIntl($this, false);
+
+ $translator = new IdentityTranslator();
+
+ \Locale::setDefault('en');
+ $this->assertEquals('en', $translator->getLocale());
+
+ \Locale::setDefault('pt_BR');
+ $this->assertEquals('pt_BR', $translator->getLocale());
+ }
+
+ public function getTransTests()
+ {
+ return array(
+ array('Symfony is great!', 'Symfony is great!', array()),
+ array('Symfony is awesome!', 'Symfony is %what%!', array('%what%' => 'awesome')),
+ );
+ }
+
+ public function getTransChoiceTests()
+ {
+ return array(
+ array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0, array('%count%' => 0)),
+ array('There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1, array('%count%' => 1)),
+ array('There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10, array('%count%' => 10)),
+ array('There are 0 apples', 'There is 1 apple|There are %count% apples', 0, array('%count%' => 0)),
+ array('There is 1 apple', 'There is 1 apple|There are %count% apples', 1, array('%count%' => 1)),
+ array('There are 10 apples', 'There is 1 apple|There are %count% apples', 10, array('%count%' => 10)),
+ // custom validation messages may be coded with a fixed value
+ array('There are 2 apples', 'There are 2 apples', 2, array('%count%' => 2)),
+ );
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Interval;
+
+class IntervalTest extends TestCase
+{
+ /**
+ * @dataProvider getTests
+ */
+ public function testTest($expected, $number, $interval)
+ {
+ $this->assertEquals($expected, Interval::test($number, $interval));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testTestException()
+ {
+ Interval::test(1, 'foobar');
+ }
+
+ public function getTests()
+ {
+ return array(
+ array(true, 3, '{1,2, 3 ,4}'),
+ array(false, 10, '{1,2, 3 ,4}'),
+ array(false, 3, '[1,2]'),
+ array(true, 1, '[1,2]'),
+ array(true, 2, '[1,2]'),
+ array(false, 1, ']1,2['),
+ array(false, 2, ']1,2['),
+ array(true, log(0), '[-Inf,2['),
+ array(true, -log(0), '[-2,+Inf]'),
+ );
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\CsvFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class CsvFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new CsvFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.csv';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadDoesNothingIfEmpty()
+ {
+ $loader = new CsvFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.csv';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array(), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new CsvFileLoader();
+ $resource = __DIR__.'/../fixtures/not-exists.csv';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadNonLocalResource()
+ {
+ $loader = new CsvFileLoader();
+ $resource = 'http://example.com/resources.csv';
+ $loader->load($resource, 'en', 'domain1');
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Translation\Loader\IcuDatFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * @requires extension intl
+ */
+class IcuDatFileLoaderTest extends LocalizedTestCase
+{
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadInvalidResource()
+ {
+ $loader = new IcuDatFileLoader();
+ $loader->load(__DIR__.'/../fixtures/resourcebundle/corrupted/resources', 'es', 'domain2');
+ }
+
+ public function testDatEnglishLoad()
+ {
+ // bundled resource is build using pkgdata command which at least in ICU 4.2 comes in extremely! buggy form
+ // you must specify an temporary build directory which is not the same as current directory and
+ // MUST reside on the same partition. pkgdata -p resources -T /srv -d.packagelist.txt
+ $loader = new IcuDatFileLoader();
+ $resource = __DIR__.'/../fixtures/resourcebundle/dat/resources';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('symfony' => 'Symfony 2 is great'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource.'.dat')), $catalogue->getResources());
+ }
+
+ public function testDatFrenchLoad()
+ {
+ $loader = new IcuDatFileLoader();
+ $resource = __DIR__.'/../fixtures/resourcebundle/dat/resources';
+ $catalogue = $loader->load($resource, 'fr', 'domain1');
+
+ $this->assertEquals(array('symfony' => 'Symfony 2 est génial'), $catalogue->all('domain1'));
+ $this->assertEquals('fr', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource.'.dat')), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new IcuDatFileLoader();
+ $loader->load(__DIR__.'/../fixtures/non-existing.txt', 'en', 'domain1');
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Translation\Loader\IcuResFileLoader;
+use Symfony\Component\Config\Resource\DirectoryResource;
+
+/**
+ * @requires extension intl
+ */
+class IcuResFileLoaderTest extends LocalizedTestCase
+{
+ public function testLoad()
+ {
+ // resource is build using genrb command
+ $loader = new IcuResFileLoader();
+ $resource = __DIR__.'/../fixtures/resourcebundle/res';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new DirectoryResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new IcuResFileLoader();
+ $loader->load(__DIR__.'/../fixtures/non-existing.txt', 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadInvalidResource()
+ {
+ $loader = new IcuResFileLoader();
+ $loader->load(__DIR__.'/../fixtures/resourcebundle/corrupted', 'en', 'domain1');
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\IniFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class IniFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new IniFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.ini';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadDoesNothingIfEmpty()
+ {
+ $loader = new IniFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.ini';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array(), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new IniFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.ini';
+ $loader->load($resource, 'en', 'domain1');
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\JsonFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class JsonFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new JsonFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.json';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadDoesNothingIfEmpty()
+ {
+ $loader = new JsonFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.json';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array(), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new JsonFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.json';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ * @expectedExceptionMessage Error parsing JSON - Syntax error, malformed JSON
+ */
+ public function testParseException()
+ {
+ $loader = new JsonFileLoader();
+ $resource = __DIR__.'/../fixtures/malformed.json';
+ $loader->load($resource, 'en', 'domain1');
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+
+abstract class LocalizedTestCase extends TestCase
+{
+ protected function setUp()
+ {
+ if (!extension_loaded('intl')) {
+ $this->markTestSkipped('Extension intl is required.');
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\MoFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class MoFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new MoFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.mo';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadPlurals()
+ {
+ $loader = new MoFileLoader();
+ $resource = __DIR__.'/../fixtures/plurals.mo';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar', 'foos' => '{0} bar|{1} bars'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new MoFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.mo';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadInvalidResource()
+ {
+ $loader = new MoFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.mo';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ public function testLoadEmptyTranslation()
+ {
+ $loader = new MoFileLoader();
+ $resource = __DIR__.'/../fixtures/empty-translation.mo';
+ $catalogue = $loader->load($resource, 'en', 'message');
+
+ $this->assertEquals(array(), $catalogue->all('message'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\PhpFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class PhpFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new PhpFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.php';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new PhpFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.php';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadThrowsAnExceptionIfFileNotLocal()
+ {
+ $loader = new PhpFileLoader();
+ $resource = 'http://example.com/resources.php';
+ $loader->load($resource, 'en', 'domain1');
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\PoFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class PoFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadPlurals()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/plurals.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar', 'foos' => 'bar|bars'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadDoesNothingIfEmpty()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array(), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.po';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ public function testLoadEmptyTranslation()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/empty-translation.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => ''), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testEscapedId()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/escaped-id.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $messages = $catalogue->all('domain1');
+ $this->assertArrayHasKey('escaped "foo"', $messages);
+ $this->assertEquals('escaped "bar"', $messages['escaped "foo"']);
+ }
+
+ public function testEscapedIdPlurals()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/escaped-id-plurals.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $messages = $catalogue->all('domain1');
+ $this->assertArrayHasKey('escaped "foo"', $messages);
+ $this->assertArrayHasKey('escaped "foos"', $messages);
+ $this->assertEquals('escaped "bar"', $messages['escaped "foo"']);
+ $this->assertEquals('escaped "bar"|escaped "bars"', $messages['escaped "foos"']);
+ }
+
+ public function testSkipFuzzyTranslations()
+ {
+ $loader = new PoFileLoader();
+ $resource = __DIR__.'/../fixtures/fuzzy-translations.po';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $messages = $catalogue->all('domain1');
+ $this->assertArrayHasKey('foo1', $messages);
+ $this->assertArrayNotHasKey('foo2', $messages);
+ $this->assertArrayHasKey('foo3', $messages);
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\QtFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class QtFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new QtFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.ts';
+ $catalogue = $loader->load($resource, 'en', 'resources');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('resources'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new QtFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.ts';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadNonLocalResource()
+ {
+ $loader = new QtFileLoader();
+ $resource = 'http://domain1.com/resources.ts';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadInvalidResource()
+ {
+ $loader = new QtFileLoader();
+ $resource = __DIR__.'/../fixtures/invalid-xml-resources.xlf';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ public function testLoadEmptyResource()
+ {
+ $loader = new QtFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.xlf';
+
+ if (method_exists($this, 'expectException')) {
+ $this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
+ $this->expectExceptionMessage(sprintf('Unable to load "%s".', $resource));
+ } else {
+ $this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s".', $resource));
+ }
+
+ $loader->load($resource, 'en', 'domain1');
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\XliffFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class XliffFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.xlf';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ $this->assertSame(array(), libxml_get_errors());
+ $this->assertContainsOnly('string', $catalogue->all('domain1'));
+ }
+
+ public function testLoadWithInternalErrorsEnabled()
+ {
+ $internalErrors = libxml_use_internal_errors(true);
+
+ $this->assertSame(array(), libxml_get_errors());
+
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.xlf';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ $this->assertSame(array(), libxml_get_errors());
+
+ libxml_clear_errors();
+ libxml_use_internal_errors($internalErrors);
+ }
+
+ public function testLoadWithExternalEntitiesDisabled()
+ {
+ $disableEntities = libxml_disable_entity_loader(true);
+
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.xlf';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ libxml_disable_entity_loader($disableEntities);
+
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadWithResname()
+ {
+ $loader = new XliffFileLoader();
+ $catalogue = $loader->load(__DIR__.'/../fixtures/resname.xlf', 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar', 'bar' => 'baz', 'baz' => 'foo'), $catalogue->all('domain1'));
+ }
+
+ public function testIncompleteResource()
+ {
+ $loader = new XliffFileLoader();
+ $catalogue = $loader->load(__DIR__.'/../fixtures/resources.xlf', 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar', 'extra' => 'extra', 'key' => '', 'test' => 'with'), $catalogue->all('domain1'));
+ }
+
+ public function testEncoding()
+ {
+ $loader = new XliffFileLoader();
+ $catalogue = $loader->load(__DIR__.'/../fixtures/encoding.xlf', 'en', 'domain1');
+
+ $this->assertEquals(utf8_decode('föö'), $catalogue->get('bar', 'domain1'));
+ $this->assertEquals(utf8_decode('bär'), $catalogue->get('foo', 'domain1'));
+ $this->assertEquals(array('notes' => array(array('content' => utf8_decode('bäz'))), 'id' => '1'), $catalogue->getMetadata('foo', 'domain1'));
+ }
+
+ public function testTargetAttributesAreStoredCorrectly()
+ {
+ $loader = new XliffFileLoader();
+ $catalogue = $loader->load(__DIR__.'/../fixtures/with-attributes.xlf', 'en', 'domain1');
+
+ $metadata = $catalogue->getMetadata('foo', 'domain1');
+ $this->assertEquals('translated', $metadata['target-attributes']['state']);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadInvalidResource()
+ {
+ $loader = new XliffFileLoader();
+ $loader->load(__DIR__.'/../fixtures/resources.php', 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadResourceDoesNotValidate()
+ {
+ $loader = new XliffFileLoader();
+ $loader->load(__DIR__.'/../fixtures/non-valid.xlf', 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.xlf';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadThrowsAnExceptionIfFileNotLocal()
+ {
+ $loader = new XliffFileLoader();
+ $resource = 'http://example.com/resources.xlf';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ * @expectedExceptionMessage Document types are not allowed.
+ */
+ public function testDocTypeIsNotAllowed()
+ {
+ $loader = new XliffFileLoader();
+ $loader->load(__DIR__.'/../fixtures/withdoctype.xlf', 'en', 'domain1');
+ }
+
+ public function testParseEmptyFile()
+ {
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.xlf';
+
+ if (method_exists($this, 'expectException')) {
+ $this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
+ $this->expectExceptionMessage(sprintf('Unable to load "%s":', $resource));
+ } else {
+ $this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s":', $resource));
+ }
+
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ public function testLoadNotes()
+ {
+ $loader = new XliffFileLoader();
+ $catalogue = $loader->load(__DIR__.'/../fixtures/withnote.xlf', 'en', 'domain1');
+
+ $this->assertEquals(array('notes' => array(array('priority' => 1, 'content' => 'foo')), 'id' => '1'), $catalogue->getMetadata('foo', 'domain1'));
+ // message without target
+ $this->assertEquals(array('notes' => array(array('content' => 'bar', 'from' => 'foo')), 'id' => '2'), $catalogue->getMetadata('extra', 'domain1'));
+ // message with empty target
+ $this->assertEquals(array('notes' => array(array('content' => 'baz'), array('priority' => 2, 'from' => 'bar', 'content' => 'qux')), 'id' => '123'), $catalogue->getMetadata('key', 'domain1'));
+ }
+
+ public function testLoadVersion2()
+ {
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/resources-2.0.xlf';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ $this->assertSame(array(), libxml_get_errors());
+
+ $domains = $catalogue->all();
+ $this->assertCount(3, $domains['domain1']);
+ $this->assertContainsOnly('string', $catalogue->all('domain1'));
+
+ // target attributes
+ $this->assertEquals(array('target-attributes' => array('order' => 1)), $catalogue->getMetadata('bar', 'domain1'));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\YamlFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class YamlFileLoaderTest extends TestCase
+{
+ public function testLoad()
+ {
+ $loader = new YamlFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.yml';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ public function testLoadDoesNothingIfEmpty()
+ {
+ $loader = new YamlFileLoader();
+ $resource = __DIR__.'/../fixtures/empty.yml';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ $this->assertEquals(array(), $catalogue->all('domain1'));
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadNonExistingResource()
+ {
+ $loader = new YamlFileLoader();
+ $resource = __DIR__.'/../fixtures/non-existing.yml';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadThrowsAnExceptionIfFileNotLocal()
+ {
+ $loader = new YamlFileLoader();
+ $resource = 'http://example.com/resources.yml';
+ $loader->load($resource, 'en', 'domain1');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+ */
+ public function testLoadThrowsAnExceptionIfNotAnArray()
+ {
+ $loader = new YamlFileLoader();
+ $resource = __DIR__.'/../fixtures/non-valid.yml';
+ $loader->load($resource, 'en', 'domain1');
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Translator;
+use Symfony\Component\Translation\LoggingTranslator;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+
+class LoggingTranslatorTest extends TestCase
+{
+ public function testTransWithNoTranslationIsLogged()
+ {
+ $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+ $logger->expects($this->exactly(2))
+ ->method('warning')
+ ->with('Translation not found.')
+ ;
+
+ $translator = new Translator('ar');
+ $loggableTranslator = new LoggingTranslator($translator, $logger);
+ $loggableTranslator->transChoice('some_message2', 10, array('%count%' => 10));
+ $loggableTranslator->trans('bar');
+ }
+
+ public function testTransChoiceFallbackIsLogged()
+ {
+ $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+ $logger->expects($this->once())
+ ->method('debug')
+ ->with('Translation use fallback catalogue.')
+ ;
+
+ $translator = new Translator('ar');
+ $translator->setFallbackLocales(array('en'));
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en');
+ $loggableTranslator = new LoggingTranslator($translator, $logger);
+ $loggableTranslator->transChoice('some_message2', 10, array('%count%' => 10));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class MessageCatalogueTest extends TestCase
+{
+ public function testGetLocale()
+ {
+ $catalogue = new MessageCatalogue('en');
+
+ $this->assertEquals('en', $catalogue->getLocale());
+ }
+
+ public function testGetDomains()
+ {
+ $catalogue = new MessageCatalogue('en', array('domain1' => array(), 'domain2' => array()));
+
+ $this->assertEquals(array('domain1', 'domain2'), $catalogue->getDomains());
+ }
+
+ public function testAll()
+ {
+ $catalogue = new MessageCatalogue('en', $messages = array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+
+ $this->assertEquals(array('foo' => 'foo'), $catalogue->all('domain1'));
+ $this->assertEquals(array(), $catalogue->all('domain88'));
+ $this->assertEquals($messages, $catalogue->all());
+ }
+
+ public function testHas()
+ {
+ $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+
+ $this->assertTrue($catalogue->has('foo', 'domain1'));
+ $this->assertFalse($catalogue->has('bar', 'domain1'));
+ $this->assertFalse($catalogue->has('foo', 'domain88'));
+ }
+
+ public function testGetSet()
+ {
+ $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+ $catalogue->set('foo1', 'foo1', 'domain1');
+
+ $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+ $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+ }
+
+ public function testAdd()
+ {
+ $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+ $catalogue->add(array('foo1' => 'foo1'), 'domain1');
+
+ $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+ $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+ $catalogue->add(array('foo' => 'bar'), 'domain1');
+ $this->assertEquals('bar', $catalogue->get('foo', 'domain1'));
+ $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+ $catalogue->add(array('foo' => 'bar'), 'domain88');
+ $this->assertEquals('bar', $catalogue->get('foo', 'domain88'));
+ }
+
+ public function testReplace()
+ {
+ $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+ $catalogue->replace($messages = array('foo1' => 'foo1'), 'domain1');
+
+ $this->assertEquals($messages, $catalogue->all('domain1'));
+ }
+
+ public function testAddCatalogue()
+ {
+ $r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
+
+ $r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
+
+ $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+ $catalogue->addResource($r);
+
+ $catalogue1 = new MessageCatalogue('en', array('domain1' => array('foo1' => 'foo1')));
+ $catalogue1->addResource($r1);
+
+ $catalogue->addCatalogue($catalogue1);
+
+ $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+ $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+ $this->assertEquals(array($r, $r1), $catalogue->getResources());
+ }
+
+ public function testAddFallbackCatalogue()
+ {
+ $r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
+
+ $r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
+
+ $r2 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r2->expects($this->any())->method('__toString')->will($this->returnValue('r2'));
+
+ $catalogue = new MessageCatalogue('fr_FR', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+ $catalogue->addResource($r);
+
+ $catalogue1 = new MessageCatalogue('fr', array('domain1' => array('foo' => 'bar', 'foo1' => 'foo1')));
+ $catalogue1->addResource($r1);
+
+ $catalogue2 = new MessageCatalogue('en');
+ $catalogue2->addResource($r2);
+
+ $catalogue->addFallbackCatalogue($catalogue1);
+ $catalogue1->addFallbackCatalogue($catalogue2);
+
+ $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+ $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+ $this->assertEquals(array($r, $r1, $r2), $catalogue->getResources());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\LogicException
+ */
+ public function testAddFallbackCatalogueWithParentCircularReference()
+ {
+ $main = new MessageCatalogue('en_US');
+ $fallback = new MessageCatalogue('fr_FR');
+
+ $fallback->addFallbackCatalogue($main);
+ $main->addFallbackCatalogue($fallback);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\LogicException
+ */
+ public function testAddFallbackCatalogueWithFallbackCircularReference()
+ {
+ $fr = new MessageCatalogue('fr');
+ $en = new MessageCatalogue('en');
+ $es = new MessageCatalogue('es');
+
+ $fr->addFallbackCatalogue($en);
+ $es->addFallbackCatalogue($en);
+ $en->addFallbackCatalogue($fr);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\LogicException
+ */
+ public function testAddCatalogueWhenLocaleIsNotTheSameAsTheCurrentOne()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->addCatalogue(new MessageCatalogue('fr', array()));
+ }
+
+ public function testGetAddResource()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
+ $catalogue->addResource($r);
+ $catalogue->addResource($r);
+ $r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+ $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
+ $catalogue->addResource($r1);
+
+ $this->assertEquals(array($r, $r1), $catalogue->getResources());
+ }
+
+ public function testMetadataDelete()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $this->assertEquals(array(), $catalogue->getMetadata('', ''), 'Metadata is empty');
+ $catalogue->deleteMetadata('key', 'messages');
+ $catalogue->deleteMetadata('', 'messages');
+ $catalogue->deleteMetadata();
+ }
+
+ public function testMetadataSetGetDelete()
+ {
+ $catalogue = new MessageCatalogue('en');
+ $catalogue->setMetadata('key', 'value');
+ $this->assertEquals('value', $catalogue->getMetadata('key', 'messages'), "Metadata 'key' = 'value'");
+
+ $catalogue->setMetadata('key2', array());
+ $this->assertEquals(array(), $catalogue->getMetadata('key2', 'messages'), 'Metadata key2 is array');
+
+ $catalogue->deleteMetadata('key2', 'messages');
+ $this->assertNull($catalogue->getMetadata('key2', 'messages'), 'Metadata key2 should is deleted.');
+
+ $catalogue->deleteMetadata('key2', 'domain');
+ $this->assertNull($catalogue->getMetadata('key2', 'domain'), 'Metadata key2 should is deleted.');
+ }
+
+ public function testMetadataMerge()
+ {
+ $cat1 = new MessageCatalogue('en');
+ $cat1->setMetadata('a', 'b');
+ $this->assertEquals(array('messages' => array('a' => 'b')), $cat1->getMetadata('', ''), 'Cat1 contains messages metadata.');
+
+ $cat2 = new MessageCatalogue('en');
+ $cat2->setMetadata('b', 'c', 'domain');
+ $this->assertEquals(array('domain' => array('b' => 'c')), $cat2->getMetadata('', ''), 'Cat2 contains domain metadata.');
+
+ $cat1->addCatalogue($cat2);
+ $this->assertEquals(array('messages' => array('a' => 'b'), 'domain' => array('b' => 'c')), $cat1->getMetadata('', ''), 'Cat1 contains merged metadata.');
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageSelector;
+
+class MessageSelectorTest extends TestCase
+{
+ /**
+ * @dataProvider getChooseTests
+ */
+ public function testChoose($expected, $id, $number)
+ {
+ $selector = new MessageSelector();
+
+ $this->assertEquals($expected, $selector->choose($id, $number, 'en'));
+ }
+
+ public function testReturnMessageIfExactlyOneStandardRuleIsGiven()
+ {
+ $selector = new MessageSelector();
+
+ $this->assertEquals('There are two apples', $selector->choose('There are two apples', 2, 'en'));
+ }
+
+ /**
+ * @dataProvider getNonMatchingMessages
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number)
+ {
+ $selector = new MessageSelector();
+
+ $selector->choose($id, $number, 'en');
+ }
+
+ public function getNonMatchingMessages()
+ {
+ return array(
+ array('{0} There are no apples|{1} There is one apple', 2),
+ array('{1} There is one apple|]1,Inf] There are %count% apples', 0),
+ array('{1} There is one apple|]2,Inf] There are %count% apples', 2),
+ array('{0} There are no apples|There is one apple', 2),
+ );
+ }
+
+ public function getChooseTests()
+ {
+ return array(
+ array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0),
+ array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0),
+ array('There are no apples', '{0}There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0),
+
+ array('There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1),
+
+ array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10),
+ array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10),
+ array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10),
+
+ array('There are %count% apples', 'There is one apple|There are %count% apples', 0),
+ array('There is one apple', 'There is one apple|There are %count% apples', 1),
+ array('There are %count% apples', 'There is one apple|There are %count% apples', 10),
+
+ array('There are %count% apples', 'one: There is one apple|more: There are %count% apples', 0),
+ array('There is one apple', 'one: There is one apple|more: There are %count% apples', 1),
+ array('There are %count% apples', 'one: There is one apple|more: There are %count% apples', 10),
+
+ array('There are no apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 0),
+ array('There is one apple', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 1),
+ array('There are %count% apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10),
+
+ array('', '{0}|{1} There is one apple|]1,Inf] There are %count% apples', 0),
+ array('', '{0} There are no apples|{1}|]1,Inf] There are %count% apples', 1),
+
+ // Indexed only tests which are Gettext PoFile* compatible strings.
+ array('There are %count% apples', 'There is one apple|There are %count% apples', 0),
+ array('There is one apple', 'There is one apple|There are %count% apples', 1),
+ array('There are %count% apples', 'There is one apple|There are %count% apples', 2),
+
+ // Tests for float numbers
+ array('There is almost one apple', '{0} There are no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7),
+ array('There is one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1),
+ array('There is more than one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7),
+ array('There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0),
+ array('There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0),
+ array('There are no apples', '{0.0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0),
+
+ // Test texts with new-lines
+ // with double-quotes and \n in id & double-quotes and actual newlines in text
+ array("This is a text with a\n new-line in it. Selector = 0.", '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 0),
+ // with double-quotes and \n in id and single-quotes and actual newlines in text
+ array("This is a text with a\n new-line in it. Selector = 1.", '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 1),
+ array("This is a text with a\n new-line in it. Selector > 1.", '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 5),
+ // with double-quotes and id split accros lines
+ array('This is a text with a
+ new-line in it. Selector = 1.', '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 1),
+ // with single-quotes and id split accros lines
+ array('This is a text with a
+ new-line in it. Selector > 1.', '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 5),
+ // with single-quotes and \n in text
+ array('This is a text with a\nnew-line in it. Selector = 0.', '{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.', 0),
+ // with double-quotes and id split accros lines
+ array("This is a text with a\nnew-line in it. Selector = 1.", "{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.", 1),
+ // esacape pipe
+ array('This is a text with | in it. Selector = 0.', '{0}This is a text with || in it. Selector = 0.|{1}This is a text with || in it. Selector = 1.', 0),
+ );
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\PluralizationRules;
+
+/**
+ * Test should cover all languages mentioned on http://translate.sourceforge.net/wiki/l10n/pluralforms
+ * and Plural forms mentioned on http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms.
+ *
+ * See also https://developer.mozilla.org/en/Localization_and_Plurals which mentions 15 rules having a maximum of 6 forms.
+ * The mozilla code is also interesting to check for.
+ *
+ * As mentioned by chx http://drupal.org/node/1273968 we can cover all by testing number from 0 to 199
+ *
+ * The goal to cover all languages is to far fetched so this test case is smaller.
+ *
+ * @author Clemens Tolboom clemens@build2be.nl
+ */
+class PluralizationRulesTest extends TestCase
+{
+ /**
+ * We test failed langcode here.
+ *
+ * TODO: The languages mentioned in the data provide need to get fixed somehow within PluralizationRules.
+ *
+ * @dataProvider failingLangcodes
+ */
+ public function testFailedLangcodes($nplural, $langCodes)
+ {
+ $matrix = $this->generateTestData($langCodes);
+ $this->validateMatrix($nplural, $matrix, false);
+ }
+
+ /**
+ * @dataProvider successLangcodes
+ */
+ public function testLangcodes($nplural, $langCodes)
+ {
+ $matrix = $this->generateTestData($langCodes);
+ $this->validateMatrix($nplural, $matrix);
+ }
+
+ /**
+ * This array should contain all currently known langcodes.
+ *
+ * As it is impossible to have this ever complete we should try as hard as possible to have it almost complete.
+ *
+ * @return array
+ */
+ public function successLangcodes()
+ {
+ return array(
+ array('1', array('ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky')),
+ array('2', array('nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM')),
+ array('3', array('be', 'bs', 'cs', 'hr')),
+ array('4', array('cy', 'mt', 'sl')),
+ array('6', array('ar')),
+ );
+ }
+
+ /**
+ * This array should be at least empty within the near future.
+ *
+ * This both depends on a complete list trying to add above as understanding
+ * the plural rules of the current failing languages.
+ *
+ * @return array with nplural together with langcodes
+ */
+ public function failingLangcodes()
+ {
+ return array(
+ array('1', array('fa')),
+ array('2', array('jbo')),
+ array('3', array('cbs')),
+ array('4', array('gd', 'kw')),
+ array('5', array('ga')),
+ );
+ }
+
+ /**
+ * We validate only on the plural coverage. Thus the real rules is not tested.
+ *
+ * @param string $nplural plural expected
+ * @param array $matrix containing langcodes and their plural index values
+ * @param bool $expectSuccess
+ */
+ protected function validateMatrix($nplural, $matrix, $expectSuccess = true)
+ {
+ foreach ($matrix as $langCode => $data) {
+ $indexes = array_flip($data);
+ if ($expectSuccess) {
+ $this->assertEquals($nplural, count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
+ } else {
+ $this->assertNotEquals((int) $nplural, count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
+ }
+ }
+ }
+
+ protected function generateTestData($langCodes)
+ {
+ $matrix = array();
+ foreach ($langCodes as $langCode) {
+ for ($count = 0; $count < 200; ++$count) {
+ $plural = PluralizationRules::get($count, $langCode);
+ $matrix[$langCode][$count] = $plural;
+ }
+ }
+
+ return $matrix;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\Loader\LoaderInterface;
+use Symfony\Component\Translation\Translator;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class TranslatorCacheTest extends TestCase
+{
+ protected $tmpDir;
+
+ protected function setUp()
+ {
+ $this->tmpDir = sys_get_temp_dir().'/sf2_translation';
+ $this->deleteTmpDir();
+ }
+
+ protected function tearDown()
+ {
+ $this->deleteTmpDir();
+ }
+
+ protected function deleteTmpDir()
+ {
+ if (!file_exists($dir = $this->tmpDir)) {
+ return;
+ }
+
+ $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->tmpDir), \RecursiveIteratorIterator::CHILD_FIRST);
+ foreach ($iterator as $path) {
+ if (preg_match('#[/\\\\]\.\.?$#', $path->__toString())) {
+ continue;
+ }
+ if ($path->isDir()) {
+ rmdir($path->__toString());
+ } else {
+ unlink($path->__toString());
+ }
+ }
+ rmdir($this->tmpDir);
+ }
+
+ /**
+ * @dataProvider runForDebugAndProduction
+ */
+ public function testThatACacheIsUsed($debug)
+ {
+ $locale = 'any_locale';
+ $format = 'some_format';
+ $msgid = 'test';
+
+ // Prime the cache
+ $translator = new Translator($locale, null, $this->tmpDir, $debug);
+ $translator->addLoader($format, new ArrayLoader());
+ $translator->addResource($format, array($msgid => 'OK'), $locale);
+ $translator->trans($msgid);
+
+ // Try again and see we get a valid result whilst no loader can be used
+ $translator = new Translator($locale, null, $this->tmpDir, $debug);
+ $translator->addLoader($format, $this->createFailingLoader());
+ $translator->addResource($format, array($msgid => 'OK'), $locale);
+ $this->assertEquals('OK', $translator->trans($msgid), '-> caching does not work in '.($debug ? 'debug' : 'production'));
+ }
+
+ public function testCatalogueIsReloadedWhenResourcesAreNoLongerFresh()
+ {
+ /*
+ * The testThatACacheIsUsed() test showed that we don't need the loader as long as the cache
+ * is fresh.
+ *
+ * Now we add a Resource that is never fresh and make sure that the
+ * cache is discarded (the loader is called twice).
+ *
+ * We need to run this for debug=true only because in production the cache
+ * will never be revalidated.
+ */
+
+ $locale = 'any_locale';
+ $format = 'some_format';
+ $msgid = 'test';
+
+ $catalogue = new MessageCatalogue($locale, array());
+ $catalogue->addResource(new StaleResource()); // better use a helper class than a mock, because it gets serialized in the cache and re-loaded
+
+ /** @var LoaderInterface|\PHPUnit_Framework_MockObject_MockObject $loader */
+ $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
+ $loader
+ ->expects($this->exactly(2))
+ ->method('load')
+ ->will($this->returnValue($catalogue))
+ ;
+
+ // 1st pass
+ $translator = new Translator($locale, null, $this->tmpDir, true);
+ $translator->addLoader($format, $loader);
+ $translator->addResource($format, null, $locale);
+ $translator->trans($msgid);
+
+ // 2nd pass
+ $translator = new Translator($locale, null, $this->tmpDir, true);
+ $translator->addLoader($format, $loader);
+ $translator->addResource($format, null, $locale);
+ $translator->trans($msgid);
+ }
+
+ /**
+ * @dataProvider runForDebugAndProduction
+ */
+ public function testDifferentTranslatorsForSameLocaleDoNotOverwriteEachOthersCache($debug)
+ {
+ /*
+ * Similar to the previous test. After we used the second translator, make
+ * sure there's still a useable cache for the first one.
+ */
+
+ $locale = 'any_locale';
+ $format = 'some_format';
+ $msgid = 'test';
+
+ // Create a Translator and prime its cache
+ $translator = new Translator($locale, null, $this->tmpDir, $debug);
+ $translator->addLoader($format, new ArrayLoader());
+ $translator->addResource($format, array($msgid => 'OK'), $locale);
+ $translator->trans($msgid);
+
+ // Create another Translator with a different catalogue for the same locale
+ $translator = new Translator($locale, null, $this->tmpDir, $debug);
+ $translator->addLoader($format, new ArrayLoader());
+ $translator->addResource($format, array($msgid => 'FAIL'), $locale);
+ $translator->trans($msgid);
+
+ // Now the first translator must still have a useable cache.
+ $translator = new Translator($locale, null, $this->tmpDir, $debug);
+ $translator->addLoader($format, $this->createFailingLoader());
+ $translator->addResource($format, array($msgid => 'OK'), $locale);
+ $this->assertEquals('OK', $translator->trans($msgid), '-> the cache was overwritten by another translator instance in '.($debug ? 'debug' : 'production'));
+ }
+
+ public function testGeneratedCacheFilesAreOnlyBelongRequestedLocales()
+ {
+ $translator = new Translator('a', null, $this->tmpDir);
+ $translator->setFallbackLocales(array('b'));
+ $translator->trans('bar');
+
+ $cachedFiles = glob($this->tmpDir.'/*.php');
+
+ $this->assertCount(1, $cachedFiles);
+ }
+
+ public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales()
+ {
+ /*
+ * Because the cache file contains a catalogue including all of its fallback
+ * catalogues, we must take the set of fallback locales into consideration when
+ * loading a catalogue from the cache.
+ */
+ $translator = new Translator('a', null, $this->tmpDir);
+ $translator->setFallbackLocales(array('b'));
+
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
+ $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
+
+ $this->assertEquals('bar (b)', $translator->trans('bar'));
+
+ // Remove fallback locale
+ $translator->setFallbackLocales(array());
+ $this->assertEquals('bar', $translator->trans('bar'));
+
+ // Use a fresh translator with no fallback locales, result should be the same
+ $translator = new Translator('a', null, $this->tmpDir);
+
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
+ $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
+
+ $this->assertEquals('bar', $translator->trans('bar'));
+ }
+
+ public function testPrimaryAndFallbackCataloguesContainTheSameMessagesRegardlessOfCaching()
+ {
+ /*
+ * As a safeguard against potential BC breaks, make sure that primary and fallback
+ * catalogues (reachable via getFallbackCatalogue()) always contain the full set of
+ * messages provided by the loader. This must also be the case when these catalogues
+ * are (internally) read from a cache.
+ *
+ * Optimizations inside the translator must not change this behaviour.
+ */
+
+ /*
+ * Create a translator that loads two catalogues for two different locales.
+ * The catalogues contain distinct sets of messages.
+ */
+ $translator = new Translator('a', null, $this->tmpDir);
+ $translator->setFallbackLocales(array('b'));
+
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
+ $translator->addResource('array', array('foo' => 'foo (b)'), 'b');
+ $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
+
+ $catalogue = $translator->getCatalogue('a');
+ $this->assertFalse($catalogue->defines('bar')); // Sure, the "a" catalogue does not contain that message.
+
+ $fallback = $catalogue->getFallbackCatalogue();
+ $this->assertTrue($fallback->defines('foo')); // "foo" is present in "a" and "b"
+
+ /*
+ * Now, repeat the same test.
+ * Behind the scenes, the cache is used. But that should not matter, right?
+ */
+ $translator = new Translator('a', null, $this->tmpDir);
+ $translator->setFallbackLocales(array('b'));
+
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
+ $translator->addResource('array', array('foo' => 'foo (b)'), 'b');
+ $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
+
+ $catalogue = $translator->getCatalogue('a');
+ $this->assertFalse($catalogue->defines('bar'));
+
+ $fallback = $catalogue->getFallbackCatalogue();
+ $this->assertTrue($fallback->defines('foo'));
+ }
+
+ public function testRefreshCacheWhenResourcesAreNoLongerFresh()
+ {
+ $resource = $this->getMockBuilder('Symfony\Component\Config\Resource\SelfCheckingResourceInterface')->getMock();
+ $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
+ $resource->method('isFresh')->will($this->returnValue(false));
+ $loader
+ ->expects($this->exactly(2))
+ ->method('load')
+ ->will($this->returnValue($this->getCatalogue('fr', array(), array($resource))));
+
+ // prime the cache
+ $translator = new Translator('fr', null, $this->tmpDir, true);
+ $translator->addLoader('loader', $loader);
+ $translator->addResource('loader', 'foo', 'fr');
+ $translator->trans('foo');
+
+ // prime the cache second time
+ $translator = new Translator('fr', null, $this->tmpDir, true);
+ $translator->addLoader('loader', $loader);
+ $translator->addResource('loader', 'foo', 'fr');
+ $translator->trans('foo');
+ }
+
+ protected function getCatalogue($locale, $messages, $resources = array())
+ {
+ $catalogue = new MessageCatalogue($locale);
+ foreach ($messages as $key => $translation) {
+ $catalogue->set($key, $translation);
+ }
+ foreach ($resources as $resource) {
+ $catalogue->addResource($resource);
+ }
+
+ return $catalogue;
+ }
+
+ public function runForDebugAndProduction()
+ {
+ return array(array(true), array(false));
+ }
+
+ /**
+ * @return LoaderInterface
+ */
+ private function createFailingLoader()
+ {
+ $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
+ $loader
+ ->expects($this->never())
+ ->method('load');
+
+ return $loader;
+ }
+}
+
+class StaleResource implements SelfCheckingResourceInterface
+{
+ public function isFresh($timestamp)
+ {
+ return false;
+ }
+
+ public function getResource()
+ {
+ }
+
+ public function __toString()
+ {
+ return '';
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Translator;
+use Symfony\Component\Translation\MessageSelector;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class TranslatorTest extends TestCase
+{
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testConstructorInvalidLocale($locale)
+ {
+ $translator = new Translator($locale, new MessageSelector());
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testConstructorValidLocale($locale)
+ {
+ $translator = new Translator($locale, new MessageSelector());
+
+ $this->assertEquals($locale, $translator->getLocale());
+ }
+
+ public function testConstructorWithoutLocale()
+ {
+ $translator = new Translator(null, new MessageSelector());
+
+ $this->assertNull($translator->getLocale());
+ }
+
+ public function testSetGetLocale()
+ {
+ $translator = new Translator('en');
+
+ $this->assertEquals('en', $translator->getLocale());
+
+ $translator->setLocale('fr');
+ $this->assertEquals('fr', $translator->getLocale());
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testSetInvalidLocale($locale)
+ {
+ $translator = new Translator('fr', new MessageSelector());
+ $translator->setLocale($locale);
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testSetValidLocale($locale)
+ {
+ $translator = new Translator($locale, new MessageSelector());
+ $translator->setLocale($locale);
+
+ $this->assertEquals($locale, $translator->getLocale());
+ }
+
+ public function testGetCatalogue()
+ {
+ $translator = new Translator('en');
+
+ $this->assertEquals(new MessageCatalogue('en'), $translator->getCatalogue());
+
+ $translator->setLocale('fr');
+ $this->assertEquals(new MessageCatalogue('fr'), $translator->getCatalogue('fr'));
+ }
+
+ public function testGetCatalogueReturnsConsolidatedCatalogue()
+ {
+ /*
+ * This will be useful once we refactor so that different domains will be loaded lazily (on-demand).
+ * In that case, getCatalogue() will probably have to load all missing domains in order to return
+ * one complete catalogue.
+ */
+
+ $locale = 'whatever';
+ $translator = new Translator($locale);
+ $translator->addLoader('loader-a', new ArrayLoader());
+ $translator->addLoader('loader-b', new ArrayLoader());
+ $translator->addResource('loader-a', array('foo' => 'foofoo'), $locale, 'domain-a');
+ $translator->addResource('loader-b', array('bar' => 'foobar'), $locale, 'domain-b');
+
+ /*
+ * Test that we get a single catalogue comprising messages
+ * from different loaders and different domains
+ */
+ $catalogue = $translator->getCatalogue($locale);
+ $this->assertTrue($catalogue->defines('foo', 'domain-a'));
+ $this->assertTrue($catalogue->defines('bar', 'domain-b'));
+ }
+
+ public function testSetFallbackLocales()
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+ $translator->addResource('array', array('bar' => 'foobar'), 'fr');
+
+ // force catalogue loading
+ $translator->trans('bar');
+
+ $translator->setFallbackLocales(array('fr'));
+ $this->assertEquals('foobar', $translator->trans('bar'));
+ }
+
+ public function testSetFallbackLocalesMultiple()
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (en)'), 'en');
+ $translator->addResource('array', array('bar' => 'bar (fr)'), 'fr');
+
+ // force catalogue loading
+ $translator->trans('bar');
+
+ $translator->setFallbackLocales(array('fr_FR', 'fr'));
+ $this->assertEquals('bar (fr)', $translator->trans('bar'));
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testSetFallbackInvalidLocales($locale)
+ {
+ $translator = new Translator('fr', new MessageSelector());
+ $translator->setFallbackLocales(array('fr', $locale));
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testSetFallbackValidLocales($locale)
+ {
+ $translator = new Translator($locale, new MessageSelector());
+ $translator->setFallbackLocales(array('fr', $locale));
+ // no assertion. this method just asserts that no exception is thrown
+ $this->addToAssertionCount(1);
+ }
+
+ public function testTransWithFallbackLocale()
+ {
+ $translator = new Translator('fr_FR');
+ $translator->setFallbackLocales(array('en'));
+
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('bar' => 'foobar'), 'en');
+
+ $this->assertEquals('foobar', $translator->trans('bar'));
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testAddResourceInvalidLocales($locale)
+ {
+ $translator = new Translator('fr', new MessageSelector());
+ $translator->addResource('array', array('foo' => 'foofoo'), $locale);
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testAddResourceValidLocales($locale)
+ {
+ $translator = new Translator('fr', new MessageSelector());
+ $translator->addResource('array', array('foo' => 'foofoo'), $locale);
+ // no assertion. this method just asserts that no exception is thrown
+ $this->addToAssertionCount(1);
+ }
+
+ public function testAddResourceAfterTrans()
+ {
+ $translator = new Translator('fr');
+ $translator->addLoader('array', new ArrayLoader());
+
+ $translator->setFallbackLocales(array('en'));
+
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+ $this->assertEquals('foofoo', $translator->trans('foo'));
+
+ $translator->addResource('array', array('bar' => 'foobar'), 'en');
+ $this->assertEquals('foobar', $translator->trans('bar'));
+ }
+
+ /**
+ * @dataProvider getTransFileTests
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testTransWithoutFallbackLocaleFile($format, $loader)
+ {
+ $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
+ $translator = new Translator('en');
+ $translator->addLoader($format, new $loaderClass());
+ $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en');
+ $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en');
+
+ // force catalogue loading
+ $translator->trans('foo');
+ }
+
+ /**
+ * @dataProvider getTransFileTests
+ */
+ public function testTransWithFallbackLocaleFile($format, $loader)
+ {
+ $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
+ $translator = new Translator('en_GB');
+ $translator->addLoader($format, new $loaderClass());
+ $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en_GB');
+ $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en', 'resources');
+
+ $this->assertEquals('bar', $translator->trans('foo', array(), 'resources'));
+ }
+
+ public function testTransWithFallbackLocaleBis()
+ {
+ $translator = new Translator('en_US');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en_US');
+ $translator->addResource('array', array('bar' => 'foobar'), 'en');
+ $this->assertEquals('foobar', $translator->trans('bar'));
+ }
+
+ public function testTransWithFallbackLocaleTer()
+ {
+ $translator = new Translator('fr_FR');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (en_US)'), 'en_US');
+ $translator->addResource('array', array('bar' => 'bar (en)'), 'en');
+
+ $translator->setFallbackLocales(array('en_US', 'en'));
+
+ $this->assertEquals('foo (en_US)', $translator->trans('foo'));
+ $this->assertEquals('bar (en)', $translator->trans('bar'));
+ }
+
+ public function testTransNonExistentWithFallback()
+ {
+ $translator = new Translator('fr');
+ $translator->setFallbackLocales(array('en'));
+ $translator->addLoader('array', new ArrayLoader());
+ $this->assertEquals('non-existent', $translator->trans('non-existent'));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Translation\Exception\RuntimeException
+ */
+ public function testWhenAResourceHasNoRegisteredLoader()
+ {
+ $translator = new Translator('en');
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+
+ $translator->trans('foo');
+ }
+
+ public function testNestedFallbackCatalogueWhenUsingMultipleLocales()
+ {
+ $translator = new Translator('fr');
+ $translator->setFallbackLocales(array('ru', 'en'));
+
+ $translator->getCatalogue('fr');
+
+ $this->assertNotNull($translator->getCatalogue('ru')->getFallbackCatalogue());
+ }
+
+ public function testFallbackCatalogueResources()
+ {
+ $translator = new Translator('en_GB', new MessageSelector());
+ $translator->addLoader('yml', new \Symfony\Component\Translation\Loader\YamlFileLoader());
+ $translator->addResource('yml', __DIR__.'/fixtures/empty.yml', 'en_GB');
+ $translator->addResource('yml', __DIR__.'/fixtures/resources.yml', 'en');
+
+ // force catalogue loading
+ $this->assertEquals('bar', $translator->trans('foo', array()));
+
+ $resources = $translator->getCatalogue('en')->getResources();
+ $this->assertCount(1, $resources);
+ $this->assertContains(__DIR__.DIRECTORY_SEPARATOR.'fixtures'.DIRECTORY_SEPARATOR.'resources.yml', $resources);
+
+ $resources = $translator->getCatalogue('en_GB')->getResources();
+ $this->assertCount(2, $resources);
+ $this->assertContains(__DIR__.DIRECTORY_SEPARATOR.'fixtures'.DIRECTORY_SEPARATOR.'empty.yml', $resources);
+ $this->assertContains(__DIR__.DIRECTORY_SEPARATOR.'fixtures'.DIRECTORY_SEPARATOR.'resources.yml', $resources);
+ }
+
+ /**
+ * @dataProvider getTransTests
+ */
+ public function testTrans($expected, $id, $translation, $parameters, $locale, $domain)
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array((string) $id => $translation), $locale, $domain);
+
+ $this->assertEquals($expected, $translator->trans($id, $parameters, $domain, $locale));
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testTransInvalidLocale($locale)
+ {
+ $translator = new Translator('en', new MessageSelector());
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+
+ $translator->trans('foo', array(), '', $locale);
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testTransValidLocale($locale)
+ {
+ $translator = new Translator($locale, new MessageSelector());
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('test' => 'OK'), $locale);
+
+ $this->assertEquals('OK', $translator->trans('test'));
+ $this->assertEquals('OK', $translator->trans('test', array(), null, $locale));
+ }
+
+ /**
+ * @dataProvider getFlattenedTransTests
+ */
+ public function testFlattenedTrans($expected, $messages, $id)
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', $messages, 'fr', '');
+
+ $this->assertEquals($expected, $translator->trans($id, array(), '', 'fr'));
+ }
+
+ /**
+ * @dataProvider getTransChoiceTests
+ */
+ public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain)
+ {
+ $translator = new Translator('en');
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array((string) $id => $translation), $locale, $domain);
+
+ $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters, $domain, $locale));
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+ */
+ public function testTransChoiceInvalidLocale($locale)
+ {
+ $translator = new Translator('en', new MessageSelector());
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+
+ $translator->transChoice('foo', 1, array(), '', $locale);
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testTransChoiceValidLocale($locale)
+ {
+ $translator = new Translator('en', new MessageSelector());
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+
+ $translator->transChoice('foo', 1, array(), '', $locale);
+ // no assertion. this method just asserts that no exception is thrown
+ $this->addToAssertionCount(1);
+ }
+
+ public function getTransFileTests()
+ {
+ return array(
+ array('csv', 'CsvFileLoader'),
+ array('ini', 'IniFileLoader'),
+ array('mo', 'MoFileLoader'),
+ array('po', 'PoFileLoader'),
+ array('php', 'PhpFileLoader'),
+ array('ts', 'QtFileLoader'),
+ array('xlf', 'XliffFileLoader'),
+ array('yml', 'YamlFileLoader'),
+ array('json', 'JsonFileLoader'),
+ );
+ }
+
+ public function getTransTests()
+ {
+ return array(
+ array('Symfony est super !', 'Symfony is great!', 'Symfony est super !', array(), 'fr', ''),
+ array('Symfony est awesome !', 'Symfony is %what%!', 'Symfony est %what% !', array('%what%' => 'awesome'), 'fr', ''),
+ array('Symfony est super !', new StringClass('Symfony is great!'), 'Symfony est super !', array(), 'fr', ''),
+ );
+ }
+
+ public function getFlattenedTransTests()
+ {
+ $messages = array(
+ 'symfony' => array(
+ 'is' => array(
+ 'great' => 'Symfony est super!',
+ ),
+ ),
+ 'foo' => array(
+ 'bar' => array(
+ 'baz' => 'Foo Bar Baz',
+ ),
+ 'baz' => 'Foo Baz',
+ ),
+ );
+
+ return array(
+ array('Symfony est super!', $messages, 'symfony.is.great'),
+ array('Foo Bar Baz', $messages, 'foo.bar.baz'),
+ array('Foo Baz', $messages, 'foo.baz'),
+ );
+ }
+
+ public function getTransChoiceTests()
+ {
+ return array(
+ array('Il y a 0 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array(), 'fr', ''),
+ array('Il y a 1 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, array(), 'fr', ''),
+ array('Il y a 10 pommes', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, array(), 'fr', ''),
+
+ array('Il y a 0 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 0, array(), 'fr', ''),
+ array('Il y a 1 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 1, array(), 'fr', ''),
+ array('Il y a 10 pommes', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 10, array(), 'fr', ''),
+
+ array('Il y a 0 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array(), 'fr', ''),
+ array('Il y a 1 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array(), 'fr', ''),
+ array('Il y a 10 pommes', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array(), 'fr', ''),
+
+ array('Il n\'y a aucune pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array(), 'fr', ''),
+ array('Il y a 1 pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array(), 'fr', ''),
+ array('Il y a 10 pommes', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array(), 'fr', ''),
+
+ array('Il y a 0 pomme', new StringClass('{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples'), '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array(), 'fr', ''),
+
+ // Override %count% with a custom value
+ array('Il y a quelques pommes', 'one: There is one apple|more: There are %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 2, array('%count%' => 'quelques'), 'fr', ''),
+ );
+ }
+
+ public function getInvalidLocalesTests()
+ {
+ return array(
+ array('fr FR'),
+ array('français'),
+ array('fr+en'),
+ array('utf#8'),
+ array('fr&en'),
+ array('fr~FR'),
+ array(' fr'),
+ array('fr '),
+ array('fr*'),
+ array('fr/FR'),
+ array('fr\\FR'),
+ );
+ }
+
+ public function getValidLocalesTests()
+ {
+ return array(
+ array(''),
+ array(null),
+ array('fr'),
+ array('francais'),
+ array('FR'),
+ array('frFR'),
+ array('fr-FR'),
+ array('fr_FR'),
+ array('fr.FR'),
+ array('fr-FR.UTF8'),
+ array('sr@latin'),
+ );
+ }
+
+ public function testTransChoiceFallback()
+ {
+ $translator = new Translator('ru');
+ $translator->setFallbackLocales(array('en'));
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en');
+
+ $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
+ }
+
+ public function testTransChoiceFallbackBis()
+ {
+ $translator = new Translator('ru');
+ $translator->setFallbackLocales(array('en_US', 'en'));
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en_US');
+
+ $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
+ }
+
+ public function testTransChoiceFallbackWithNoTranslation()
+ {
+ $translator = new Translator('ru');
+ $translator->setFallbackLocales(array('en'));
+ $translator->addLoader('array', new ArrayLoader());
+
+ // consistent behavior with Translator::trans(), which returns the string
+ // unchanged if it can't be found
+ $this->assertEquals('some_message2', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
+ }
+}
+
+class StringClass
+{
+ protected $str;
+
+ public function __construct($str)
+ {
+ $this->str = $str;
+ }
+
+ public function __toString()
+ {
+ return $this->str;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Util;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Util\ArrayConverter;
+
+class ArrayConverterTest extends TestCase
+{
+ /**
+ * @dataProvider messagesData
+ */
+ public function testDump($input, $expectedOutput)
+ {
+ $this->assertEquals($expectedOutput, ArrayConverter::expandToTree($input));
+ }
+
+ public function messagesData()
+ {
+ return array(
+ array(
+ // input
+ array(
+ 'foo1' => 'bar',
+ 'foo.bar' => 'value',
+ ),
+ // expected output
+ array(
+ 'foo1' => 'bar',
+ 'foo' => array('bar' => 'value'),
+ ),
+ ),
+ array(
+ // input
+ array(
+ 'foo.bar' => 'value1',
+ 'foo.bar.test' => 'value2',
+ ),
+ // expected output
+ array(
+ 'foo' => array(
+ 'bar' => 'value1',
+ 'bar.test' => 'value2',
+ ),
+ ),
+ ),
+ array(
+ // input
+ array(
+ 'foo.level2.level3.level4' => 'value1',
+ 'foo.level2' => 'value2',
+ 'foo.bar' => 'value3',
+ ),
+ // expected output
+ array(
+ 'foo' => array(
+ 'level2' => 'value2',
+ 'level2.level3.level4' => 'value1',
+ 'bar' => 'value3',
+ ),
+ ),
+ ),
+ );
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Writer;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\DumperInterface;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Writer\TranslationWriter;
+
+class TranslationWriterTest extends TestCase
+{
+ public function testWriteTranslations()
+ {
+ $dumper = $this->getMockBuilder('Symfony\Component\Translation\Dumper\DumperInterface')->getMock();
+ $dumper
+ ->expects($this->once())
+ ->method('dump');
+
+ $writer = new TranslationWriter();
+ $writer->addDumper('test', $dumper);
+ $writer->writeTranslations(new MessageCatalogue(array()), 'test');
+ }
+
+ public function testDisableBackup()
+ {
+ $nonBackupDumper = new NonBackupDumper();
+ $backupDumper = new BackupDumper();
+
+ $writer = new TranslationWriter();
+ $writer->addDumper('non_backup', $nonBackupDumper);
+ $writer->addDumper('backup', $backupDumper);
+ $writer->disableBackup();
+
+ $this->assertFalse($backupDumper->backup, 'backup can be disabled if setBackup() method does exist');
+ }
+}
+
+class NonBackupDumper implements DumperInterface
+{
+ public function dump(MessageCatalogue $messages, $options = array())
+ {
+ }
+}
+
+class BackupDumper implements DumperInterface
+{
+ public $backup = true;
+
+ public function dump(MessageCatalogue $messages, $options = array())
+ {
+ }
+
+ public function setBackup($backup)
+ {
+ $this->backup = $backup;
+ }
+}
--- /dev/null
+msgid "foo"
+msgstr ""
+
--- /dev/null
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+ <file source-language="en" datatype="plaintext" original="file.ext">
+ <body>
+ <trans-unit id="1" resname="foo">
+ <source>foo</source>
+ <target>bär</target>
+ <note>bäz</note>
+ </trans-unit>
+ <trans-unit id="2" resname="bar">
+ <source>bar</source>
+ <target>föö</target>
+ </trans-unit>
+ </body>
+ </file>
+</xliff>
--- /dev/null
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en\n"
+
+msgid "escaped \"foo\""
+msgid_plural "escaped \"foos\""
+msgstr[0] "escaped \"bar\""
+msgstr[1] "escaped \"bars\""
--- /dev/null
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en\n"
+
+msgid "escaped \"foo\""
+msgstr "escaped \"bar\""
--- /dev/null
+#, php-format
+msgid "foo1"
+msgstr "bar1"
+
+#, fuzzy, php-format
+msgid "foo2"
+msgstr "fuzzy bar2"
+
+msgid "foo3"
+msgstr "bar3"
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+ <file source-language="en" datatype="plaintext" original="file.ext">
+ <body>
+ <trans-unit id="1">
+ <source>foo</source>
+ <target>bar
+ </trans-unit>
+ <trans-unit id="2">
+ <source>extra</source>
+ </trans-unit>
+ <trans-unit id="3">
+ <source>key</source>
+ <target></target>
+ </trans-unit>
+ <trans-unit id="4">
+ <source>test</source>
+ <target>with</target>
+ <note>note</note>
+ </trans-unit>
+ </body>
+ </file>
+</xliff>
--- /dev/null
+{
+ "foo" "bar"
+}
\ No newline at end of file
--- /dev/null
+foo:
+ bar1: value1
+ bar2: value2
--- /dev/null
+foo.bar1: value1
+foo.bar2: value2
--- /dev/null
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+ <file source-language="en" datatype="plaintext" original="file.ext">
+ <body>
+ <trans-unit>
+ <source>foo</source>
+ <target>bar</target>
+ </trans-unit>
+ </body>
+ </file>
+</xliff>
--- /dev/null
+msgid "foo"
+msgid_plural "foos"
+msgstr[0] "bar"
+msgstr[1] "bars"
+
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+ <file source-language="en" datatype="plaintext" original="file.ext">
+ <body>
+ <trans-unit id="1" resname="foo">
+ <source></source>
+ <target>bar</target>
+ </trans-unit>
+ <trans-unit id="2" resname="bar">
+ <source>bar source</source>
+ <target>baz</target>
+ </trans-unit>
+ <trans-unit id="3">
+ <source>baz</source>
+ <target>foo</target>
+ </trans-unit>
+ </body>
+ </file>
+</xliff>
--- /dev/null
+XXX
\ No newline at end of file
--- /dev/null
+en{
+ symfony{"Symfony is great"}
+}
\ No newline at end of file
--- /dev/null
+fr{
+ symfony{"Symfony est génial"}
+}
\ No newline at end of file
--- /dev/null
+en.res
+fr.res
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="fr-FR" trgLang="en-US">
+ <file id="messages.en_US">
+ <unit id="acbd18db4cc2f85cedef654fccc4a4d8">
+ <segment>
+ <source>foo</source>
+ <target>bar</target>
+ </segment>
+ </unit>
+ <unit id="3c6e0b8a9c15224a8228b9a98ca1531d">
+ <segment>
+ <source>key</source>
+ <target order="1"></target>
+ </segment>
+ </unit>
+ <unit id="18e6a493872558d949b4c16ea1fa6ab6">
+ <segment>
+ <source>key.with.cdata</source>
+ <target><![CDATA[<source> & <target>]]></target>
+ </segment>
+ </unit>
+ </file>
+</xliff>
--- /dev/null
+<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en-US" trgLang="ja-JP">
+ <file id="f1" original="Graphic Example.psd">
+ <skeleton href="Graphic Example.psd.skl"/>
+ <unit id="1">
+ <segment>
+ <source>Quetzal</source>
+ <target>Quetzal</target>
+ </segment>
+ </unit>
+ <group id="1">
+ <unit id="2">
+ <segment>
+ <source>foo</source>
+ <target>XLIFF 文書を編集、または処理 するアプリケーションです。</target>
+ </segment>
+ </unit>
+ <unit id="3">
+ <segment>
+ <source>bar</source>
+ <target order="1">XLIFF データ・マネージャ</target>
+ </segment>
+ </unit>
+ </group>
+ </file>
+</xliff>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+ <file source-language="fr-FR" target-language="en-US" datatype="plaintext" original="file.ext">
+ <header>
+ <tool tool-id="symfony" tool-name="Symfony"/>
+ </header>
+ <body>
+ <trans-unit id="acbd18db4cc2f85cedef654fccc4a4d8" resname="foo">
+ <source>foo</source>
+ <target>bar</target>
+ <note priority="1" from="bar">baz</note>
+ </trans-unit>
+ <trans-unit id="3c6e0b8a9c15224a8228b9a98ca1531d" resname="key">
+ <source>key</source>
+ <target></target>
+ <note>baz</note>
+ <note>qux</note>
+ </trans-unit>
+ <trans-unit id="18e6a493872558d949b4c16ea1fa6ab6" resname="key.with.cdata">
+ <source>key.with.cdata</source>
+ <target><![CDATA[<source> & <target>]]></target>
+ </trans-unit>
+ </body>
+ </file>
+</xliff>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+ <file source-language="fr-FR" target-language="en-US" datatype="plaintext" original="file.ext">
+ <header>
+ <tool tool-id="symfony" tool-name="Symfony"/>
+ </header>
+ <body>
+ <trans-unit id="acbd18db4cc2f85cedef654fccc4a4d8" resname="foo">
+ <source>foo</source>
+ <target state="needs-translation">bar</target>
+ </trans-unit>
+ </body>
+ </file>
+</xliff>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+ <file source-language="en-US" target-language="en-US" datatype="plaintext" original="file.ext">
+ <header>
+ <tool tool-id="foo" tool-name="foo" tool-version="0.0" tool-company="Foo"/>
+ </header>
+ <body>
+ <trans-unit id="acbd18db4cc2f85cedef654fccc4a4d8" resname="foo">
+ <source>foo</source>
+ <target>bar</target>
+ </trans-unit>
+ </body>
+ </file>
+</xliff>
--- /dev/null
+"foo"; "bar"
+#"bar"; "foo"
+"incorrect"; "number"; "columns"; "will"; "be"; "ignored"
+"incorrect"
\ No newline at end of file
--- /dev/null
+{"foo":"\u0022bar\u0022"}
\ No newline at end of file
--- /dev/null
+{
+ "foo": "bar"
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+return array (
+ 'foo' => 'bar',
+);
--- /dev/null
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en\n"
+
+msgid "foo"
+msgstr "bar"
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<TS>
+ <context>
+ <name>resources</name>
+ <message>
+ <source>foo</source>
+ <translation>bar</translation>
+ </message>
+ </context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+ <file source-language="en" datatype="plaintext" original="file.ext">
+ <body>
+ <trans-unit id="1">
+ <source>foo</source>
+ <target>bar</target>
+ </trans-unit>
+ <trans-unit id="2">
+ <source>extra</source>
+ </trans-unit>
+ <trans-unit id="3">
+ <source>key</source>
+ <target></target>
+ </trans-unit>
+ <trans-unit id="4">
+ <source>test</source>
+ <target>with</target>
+ <note>note</note>
+ </trans-unit>
+ </body>
+ </file>
+</xliff>
--- /dev/null
+foo;bar
+bar;"foo
+foo"
+"foo;foo";bar
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+ <file source-language="en" datatype="plaintext" original="file.ext">
+ <body>
+ <trans-unit id="1">
+ <source>foo</source>
+ <target state="translated">bar</target>
+ </trans-unit>
+ <trans-unit id="2">
+ <source>extra</source>
+ <target state="needs-translation">bar</target>
+ </trans-unit>
+ <trans-unit id="3">
+ <source>key</source>
+ <target></target>
+ <note>baz</note>
+ <note priority="2" from="bar">qux</note>
+ </trans-unit>
+ </body>
+ </file>
+</xliff>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE foo>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+ <file source-language="en" datatype="plaintext" original="file.ext">
+ <body>
+ <trans-unit id="1">
+ <source>foo</source>
+ <target>bar</target>
+ </trans-unit>
+ </body>
+ </file>
+</xliff>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+ <file source-language="en" datatype="plaintext" original="file.ext">
+ <body>
+ <trans-unit id="1">
+ <source>foo</source>
+ <target>bar</target>
+ <note priority="1">foo</note>
+ </trans-unit>
+ <trans-unit id="2">
+ <source>extra</source>
+ <note from="foo">bar</note>
+ </trans-unit>
+ <trans-unit id="123">
+ <source>key</source>
+ <target></target>
+ <note>baz</note>
+ <note priority="2" from="bar">qux</note>
+ </trans-unit>
+ </body>
+ </file>
+</xliff>
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Translation\Loader\LoaderInterface;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\RuntimeException;
+use Symfony\Component\Config\ConfigCacheInterface;
+use Symfony\Component\Config\ConfigCacheFactoryInterface;
+use Symfony\Component\Config\ConfigCacheFactory;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Translator implements TranslatorInterface, TranslatorBagInterface
+{
+ /**
+ * @var MessageCatalogueInterface[]
+ */
+ protected $catalogues = array();
+
+ /**
+ * @var string
+ */
+ private $locale;
+
+ /**
+ * @var array
+ */
+ private $fallbackLocales = array();
+
+ /**
+ * @var LoaderInterface[]
+ */
+ private $loaders = array();
+
+ /**
+ * @var array
+ */
+ private $resources = array();
+
+ /**
+ * @var MessageSelector
+ */
+ private $selector;
+
+ /**
+ * @var string
+ */
+ private $cacheDir;
+
+ /**
+ * @var bool
+ */
+ private $debug;
+
+ /**
+ * @var ConfigCacheFactoryInterface|null
+ */
+ private $configCacheFactory;
+
+ /**
+ * @param string $locale The locale
+ * @param MessageSelector|null $selector The message selector for pluralization
+ * @param string|null $cacheDir The directory to use for the cache
+ * @param bool $debug Use cache in debug mode ?
+ *
+ * @throws InvalidArgumentException If a locale contains invalid characters
+ */
+ public function __construct($locale, MessageSelector $selector = null, $cacheDir = null, $debug = false)
+ {
+ $this->setLocale($locale);
+ $this->selector = $selector ?: new MessageSelector();
+ $this->cacheDir = $cacheDir;
+ $this->debug = $debug;
+ }
+
+ /**
+ * Sets the ConfigCache factory to use.
+ *
+ * @param ConfigCacheFactoryInterface $configCacheFactory
+ */
+ public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
+ {
+ $this->configCacheFactory = $configCacheFactory;
+ }
+
+ /**
+ * Adds a Loader.
+ *
+ * @param string $format The name of the loader (@see addResource())
+ * @param LoaderInterface $loader A LoaderInterface instance
+ */
+ public function addLoader($format, LoaderInterface $loader)
+ {
+ $this->loaders[$format] = $loader;
+ }
+
+ /**
+ * Adds a Resource.
+ *
+ * @param string $format The name of the loader (@see addLoader())
+ * @param mixed $resource The resource name
+ * @param string $locale The locale
+ * @param string $domain The domain
+ *
+ * @throws InvalidArgumentException If the locale contains invalid characters
+ */
+ public function addResource($format, $resource, $locale, $domain = null)
+ {
+ if (null === $domain) {
+ $domain = 'messages';
+ }
+
+ $this->assertValidLocale($locale);
+
+ $this->resources[$locale][] = array($format, $resource, $domain);
+
+ if (in_array($locale, $this->fallbackLocales)) {
+ $this->catalogues = array();
+ } else {
+ unset($this->catalogues[$locale]);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLocale($locale)
+ {
+ $this->assertValidLocale($locale);
+ $this->locale = $locale;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLocale()
+ {
+ return $this->locale;
+ }
+
+ /**
+ * Sets the fallback locales.
+ *
+ * @param array $locales The fallback locales
+ *
+ * @throws InvalidArgumentException If a locale contains invalid characters
+ */
+ public function setFallbackLocales(array $locales)
+ {
+ // needed as the fallback locales are linked to the already loaded catalogues
+ $this->catalogues = array();
+
+ foreach ($locales as $locale) {
+ $this->assertValidLocale($locale);
+ }
+
+ $this->fallbackLocales = $locales;
+ }
+
+ /**
+ * Gets the fallback locales.
+ *
+ * @return array $locales The fallback locales
+ */
+ public function getFallbackLocales()
+ {
+ return $this->fallbackLocales;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function trans($id, array $parameters = array(), $domain = null, $locale = null)
+ {
+ if (null === $domain) {
+ $domain = 'messages';
+ }
+
+ return strtr($this->getCatalogue($locale)->get((string) $id, $domain), $parameters);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
+ {
+ $parameters = array_merge(array(
+ '%count%' => $number,
+ ), $parameters);
+
+ if (null === $domain) {
+ $domain = 'messages';
+ }
+
+ $id = (string) $id;
+ $catalogue = $this->getCatalogue($locale);
+ $locale = $catalogue->getLocale();
+ while (!$catalogue->defines($id, $domain)) {
+ if ($cat = $catalogue->getFallbackCatalogue()) {
+ $catalogue = $cat;
+ $locale = $catalogue->getLocale();
+ } else {
+ break;
+ }
+ }
+
+ return strtr($this->selector->choose($catalogue->get($id, $domain), (int) $number, $locale), $parameters);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCatalogue($locale = null)
+ {
+ if (null === $locale) {
+ $locale = $this->getLocale();
+ } else {
+ $this->assertValidLocale($locale);
+ }
+
+ if (!isset($this->catalogues[$locale])) {
+ $this->loadCatalogue($locale);
+ }
+
+ return $this->catalogues[$locale];
+ }
+
+ /**
+ * Gets the loaders.
+ *
+ * @return array LoaderInterface[]
+ */
+ protected function getLoaders()
+ {
+ return $this->loaders;
+ }
+
+ /**
+ * @param string $locale
+ */
+ protected function loadCatalogue($locale)
+ {
+ if (null === $this->cacheDir) {
+ $this->initializeCatalogue($locale);
+ } else {
+ $this->initializeCacheCatalogue($locale);
+ }
+ }
+
+ /**
+ * @param string $locale
+ */
+ protected function initializeCatalogue($locale)
+ {
+ $this->assertValidLocale($locale);
+
+ try {
+ $this->doLoadCatalogue($locale);
+ } catch (NotFoundResourceException $e) {
+ if (!$this->computeFallbackLocales($locale)) {
+ throw $e;
+ }
+ }
+ $this->loadFallbackCatalogues($locale);
+ }
+
+ /**
+ * @param string $locale
+ */
+ private function initializeCacheCatalogue($locale)
+ {
+ if (isset($this->catalogues[$locale])) {
+ /* Catalogue already initialized. */
+ return;
+ }
+
+ $this->assertValidLocale($locale);
+ $cache = $this->getConfigCacheFactory()->cache($this->getCatalogueCachePath($locale),
+ function (ConfigCacheInterface $cache) use ($locale) {
+ $this->dumpCatalogue($locale, $cache);
+ }
+ );
+
+ if (isset($this->catalogues[$locale])) {
+ /* Catalogue has been initialized as it was written out to cache. */
+ return;
+ }
+
+ /* Read catalogue from cache. */
+ $this->catalogues[$locale] = include $cache->getPath();
+ }
+
+ private function dumpCatalogue($locale, ConfigCacheInterface $cache)
+ {
+ $this->initializeCatalogue($locale);
+ $fallbackContent = $this->getFallbackContent($this->catalogues[$locale]);
+
+ $content = sprintf(<<<EOF
+<?php
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+\$catalogue = new MessageCatalogue('%s', %s);
+
+%s
+return \$catalogue;
+
+EOF
+ ,
+ $locale,
+ var_export($this->catalogues[$locale]->all(), true),
+ $fallbackContent
+ );
+
+ $cache->write($content, $this->catalogues[$locale]->getResources());
+ }
+
+ private function getFallbackContent(MessageCatalogue $catalogue)
+ {
+ $fallbackContent = '';
+ $current = '';
+ $replacementPattern = '/[^a-z0-9_]/i';
+ $fallbackCatalogue = $catalogue->getFallbackCatalogue();
+ while ($fallbackCatalogue) {
+ $fallback = $fallbackCatalogue->getLocale();
+ $fallbackSuffix = ucfirst(preg_replace($replacementPattern, '_', $fallback));
+ $currentSuffix = ucfirst(preg_replace($replacementPattern, '_', $current));
+
+ $fallbackContent .= sprintf(<<<'EOF'
+$catalogue%s = new MessageCatalogue('%s', %s);
+$catalogue%s->addFallbackCatalogue($catalogue%s);
+
+EOF
+ ,
+ $fallbackSuffix,
+ $fallback,
+ var_export($fallbackCatalogue->all(), true),
+ $currentSuffix,
+ $fallbackSuffix
+ );
+ $current = $fallbackCatalogue->getLocale();
+ $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue();
+ }
+
+ return $fallbackContent;
+ }
+
+ private function getCatalogueCachePath($locale)
+ {
+ return $this->cacheDir.'/catalogue.'.$locale.'.'.sha1(serialize($this->fallbackLocales)).'.php';
+ }
+
+ private function doLoadCatalogue($locale)
+ {
+ $this->catalogues[$locale] = new MessageCatalogue($locale);
+
+ if (isset($this->resources[$locale])) {
+ foreach ($this->resources[$locale] as $resource) {
+ if (!isset($this->loaders[$resource[0]])) {
+ throw new RuntimeException(sprintf('The "%s" translation loader is not registered.', $resource[0]));
+ }
+ $this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2]));
+ }
+ }
+ }
+
+ private function loadFallbackCatalogues($locale)
+ {
+ $current = $this->catalogues[$locale];
+
+ foreach ($this->computeFallbackLocales($locale) as $fallback) {
+ if (!isset($this->catalogues[$fallback])) {
+ $this->initializeCatalogue($fallback);
+ }
+
+ $fallbackCatalogue = new MessageCatalogue($fallback, $this->catalogues[$fallback]->all());
+ foreach ($this->catalogues[$fallback]->getResources() as $resource) {
+ $fallbackCatalogue->addResource($resource);
+ }
+ $current->addFallbackCatalogue($fallbackCatalogue);
+ $current = $fallbackCatalogue;
+ }
+ }
+
+ protected function computeFallbackLocales($locale)
+ {
+ $locales = array();
+ foreach ($this->fallbackLocales as $fallback) {
+ if ($fallback === $locale) {
+ continue;
+ }
+
+ $locales[] = $fallback;
+ }
+
+ if (false !== strrchr($locale, '_')) {
+ array_unshift($locales, substr($locale, 0, -strlen(strrchr($locale, '_'))));
+ }
+
+ return array_unique($locales);
+ }
+
+ /**
+ * Asserts that the locale is valid, throws an Exception if not.
+ *
+ * @param string $locale Locale to tests
+ *
+ * @throws InvalidArgumentException If the locale contains invalid characters
+ */
+ protected function assertValidLocale($locale)
+ {
+ if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
+ throw new InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale));
+ }
+ }
+
+ /**
+ * Provides the ConfigCache factory implementation, falling back to a
+ * default implementation if necessary.
+ *
+ * @return ConfigCacheFactoryInterface $configCacheFactory
+ */
+ private function getConfigCacheFactory()
+ {
+ if (!$this->configCacheFactory) {
+ $this->configCacheFactory = new ConfigCacheFactory($this->debug);
+ }
+
+ return $this->configCacheFactory;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * TranslatorBagInterface.
+ *
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+interface TranslatorBagInterface
+{
+ /**
+ * Gets the catalogue by locale.
+ *
+ * @param string|null $locale The locale or null to use the default
+ *
+ * @return MessageCatalogueInterface
+ *
+ * @throws InvalidArgumentException If the locale contains invalid characters
+ */
+ public function getCatalogue($locale = null);
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * TranslatorInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface TranslatorInterface
+{
+ /**
+ * Translates the given message.
+ *
+ * @param string $id The message id (may also be an object that can be cast to string)
+ * @param array $parameters An array of parameters for the message
+ * @param string|null $domain The domain for the message or null to use the default
+ * @param string|null $locale The locale or null to use the default
+ *
+ * @return string The translated string
+ *
+ * @throws InvalidArgumentException If the locale contains invalid characters
+ */
+ public function trans($id, array $parameters = array(), $domain = null, $locale = null);
+
+ /**
+ * Translates the given choice message by choosing a translation according to a number.
+ *
+ * @param string $id The message id (may also be an object that can be cast to string)
+ * @param int $number The number to use to find the indice of the message
+ * @param array $parameters An array of parameters for the message
+ * @param string|null $domain The domain for the message or null to use the default
+ * @param string|null $locale The locale or null to use the default
+ *
+ * @return string The translated string
+ *
+ * @throws InvalidArgumentException If the locale contains invalid characters
+ */
+ public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null);
+
+ /**
+ * Sets the current locale.
+ *
+ * @param string $locale The locale
+ *
+ * @throws InvalidArgumentException If the locale contains invalid characters
+ */
+ public function setLocale($locale);
+
+ /**
+ * Returns the current locale.
+ *
+ * @return string The locale
+ */
+ public function getLocale();
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Util;
+
+/**
+ * ArrayConverter generates tree like structure from a message catalogue.
+ * e.g. this
+ * 'foo.bar1' => 'test1',
+ * 'foo.bar2' => 'test2'
+ * converts to follows:
+ * foo:
+ * bar1: test1
+ * bar2: test2.
+ *
+ * @author Gennady Telegin <gtelegin@gmail.com>
+ */
+class ArrayConverter
+{
+ /**
+ * Converts linear messages array to tree-like array.
+ * For example this rray('foo.bar' => 'value') will be converted to array('foo' => array('bar' => 'value')).
+ *
+ * @param array $messages Linear messages array
+ *
+ * @return array Tree-like messages array
+ */
+ public static function expandToTree(array $messages)
+ {
+ $tree = array();
+
+ foreach ($messages as $id => $value) {
+ $referenceToElement = &self::getElementByPath($tree, explode('.', $id));
+
+ $referenceToElement = $value;
+
+ unset($referenceToElement);
+ }
+
+ return $tree;
+ }
+
+ private static function &getElementByPath(array &$tree, array $parts)
+ {
+ $elem = &$tree;
+ $parentOfElem = null;
+
+ foreach ($parts as $i => $part) {
+ if (isset($elem[$part]) && is_string($elem[$part])) {
+ /* Process next case:
+ * 'foo': 'test1',
+ * 'foo.bar': 'test2'
+ *
+ * $tree['foo'] was string before we found array {bar: test2}.
+ * Treat new element as string too, e.g. add $tree['foo.bar'] = 'test2';
+ */
+ $elem = &$elem[implode('.', array_slice($parts, $i))];
+ break;
+ }
+ $parentOfElem = &$elem;
+ $elem = &$elem[$part];
+ }
+
+ if (is_array($elem) && count($elem) > 0 && $parentOfElem) {
+ /* Process next case:
+ * 'foo.bar': 'test1'
+ * 'foo': 'test2'
+ *
+ * $tree['foo'] was array = {bar: 'test1'} before we found string constant `foo`.
+ * Cancel treating $tree['foo'] as array and cancel back it expansion,
+ * e.g. make it $tree['foo.bar'] = 'test1' again.
+ */
+ self::cancelExpand($parentOfElem, $part, $elem);
+ }
+
+ return $elem;
+ }
+
+ private static function cancelExpand(array &$tree, $prefix, array $node)
+ {
+ $prefix .= '.';
+
+ foreach ($node as $id => $value) {
+ if (is_string($value)) {
+ $tree[$prefix.$id] = $value;
+ } else {
+ self::cancelExpand($tree, $prefix.$id, $value);
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Writer;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\DumperInterface;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\RuntimeException;
+
+/**
+ * TranslationWriter writes translation messages.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class TranslationWriter
+{
+ /**
+ * Dumpers used for export.
+ *
+ * @var array
+ */
+ private $dumpers = array();
+
+ /**
+ * Adds a dumper to the writer.
+ *
+ * @param string $format The format of the dumper
+ * @param DumperInterface $dumper The dumper
+ */
+ public function addDumper($format, DumperInterface $dumper)
+ {
+ $this->dumpers[$format] = $dumper;
+ }
+
+ /**
+ * Disables dumper backup.
+ */
+ public function disableBackup()
+ {
+ foreach ($this->dumpers as $dumper) {
+ if (method_exists($dumper, 'setBackup')) {
+ $dumper->setBackup(false);
+ }
+ }
+ }
+
+ /**
+ * Obtains the list of supported formats.
+ *
+ * @return array
+ */
+ public function getFormats()
+ {
+ return array_keys($this->dumpers);
+ }
+
+ /**
+ * Writes translation from the catalogue according to the selected format.
+ *
+ * @param MessageCatalogue $catalogue The message catalogue to dump
+ * @param string $format The format to use to dump the messages
+ * @param array $options Options that are passed to the dumper
+ *
+ * @throws InvalidArgumentException
+ */
+ public function writeTranslations(MessageCatalogue $catalogue, $format, $options = array())
+ {
+ if (!isset($this->dumpers[$format])) {
+ throw new InvalidArgumentException(sprintf('There is no dumper associated with format "%s".', $format));
+ }
+
+ // get the right dumper
+ $dumper = $this->dumpers[$format];
+
+ if (isset($options['path']) && !is_dir($options['path']) && !@mkdir($options['path'], 0777, true) && !is_dir($options['path'])) {
+ throw new RuntimeException(sprintf('Translation Writer was not able to create directory "%s"', $options['path']));
+ }
+
+ // save
+ $dumper->dump($catalogue, $options);
+ }
+}
--- /dev/null
+{
+ "name": "symfony/translation",
+ "type": "library",
+ "description": "Symfony Translation Component",
+ "keywords": [],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": "^5.5.9|>=7.0.8",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "require-dev": {
+ "symfony/config": "~2.8|~3.0",
+ "symfony/intl": "^2.8.18|^3.2.5",
+ "symfony/yaml": "~3.3",
+ "psr/log": "~1.0"
+ },
+ "conflict": {
+ "symfony/config": "<2.8",
+ "symfony/yaml": "<3.3"
+ },
+ "suggest": {
+ "symfony/config": "",
+ "symfony/yaml": "",
+ "psr/log": "To use logging capability in translator"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Component\\Translation\\": "" },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.3-dev"
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
+ backupGlobals="false"
+ colors="true"
+ bootstrap="vendor/autoload.php"
+ failOnRisky="true"
+ failOnWarning="true"
+>
+ <php>
+ <ini name="error_reporting" value="-1" />
+ </php>
+
+ <testsuites>
+ <testsuite name="Symfony Translation Component Test Suite">
+ <directory>./Tests/</directory>
+ </testsuite>
+ </testsuites>
+
+ <filter>
+ <whitelist>
+ <directory>./</directory>
+ <exclude>
+ <directory>./Tests</directory>
+ <directory>./vendor</directory>
+ </exclude>
+ </whitelist>
+ </filter>
+</phpunit>