From 3cc54fe03a3a52cc5205e33c92fbc9b0ed5238ad Mon Sep 17 00:00:00 2001
From: jrfnl <github_nospam@adviesenzo.nl>
Date: Mon, 18 Jul 2016 00:49:58 +0200
Subject: [PATCH] Copy in the PregReplaceEModifierSniff from the
 PHPCompatibility Sniff library.

* Copy in the PregReplaceEModifierSniff.
* Copy in the related parent class PHPCompatibility_Sniff.
* Renamed both class to follow the PSR1 autoloading pattern.
* Added a readme to the PHPCompatibility sniff folder about its usage.
* Excluded the PHPCompatibility folder from the PHPCS code style checks - this way we can just drop-in replace the file (ok, except for the class renaming) when there is a new version available.
---
 WordPress-Core/ruleset.xml                    |   4 +
 .../PregReplaceEModifierSniff.php             | 112 ++++++++++++++++++
 WordPress/Sniffs/PHPCompatibility/README.md   |  24 ++++
 WordPress/Sniffs/PHPCompatibility/Sniff.php   | 106 +++++++++++++++++
 bin/phpcs.xml                                 |   2 +
 5 files changed, 248 insertions(+)
 create mode 100644 WordPress/Sniffs/PHPCompatibility/PregReplaceEModifierSniff.php
 create mode 100644 WordPress/Sniffs/PHPCompatibility/README.md
 create mode 100644 WordPress/Sniffs/PHPCompatibility/Sniff.php

diff --git a/WordPress-Core/ruleset.xml b/WordPress-Core/ruleset.xml
index e47cd4523a..8411160b79 100644
--- a/WordPress-Core/ruleset.xml
+++ b/WordPress-Core/ruleset.xml
@@ -114,5 +114,9 @@
 	<rule ref="WordPress.Functions.DontExtract"/>
 
 	<rule ref="WordPress.PHP.POSIXFunctions" />
+	<rule ref="WordPress.PHPCompatibility.PregReplaceEModifier">
+		<message>Never use preg_replace() with the /e switch, use preg_replace_callback instead.</message>
+		<type>error</type>
+	</rule>
 
 </ruleset>
diff --git a/WordPress/Sniffs/PHPCompatibility/PregReplaceEModifierSniff.php b/WordPress/Sniffs/PHPCompatibility/PregReplaceEModifierSniff.php
new file mode 100644
index 0000000000..c3f685dc3c
--- /dev/null
+++ b/WordPress/Sniffs/PHPCompatibility/PregReplaceEModifierSniff.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * PHPCompatibility_Sniffs_PHP_PregReplaceEModifierSniff.
+ *
+ * PHP version 5.6
+ *
+ * @category  PHP
+ * @package   PHPCompatibility
+ * @author    Wim Godden <wim.godden@cu.be>
+ * @copyright 2014 Cu.be Solutions bvba
+ */
+
+/**
+ * PHPCompatibility_Sniffs_PHP_PregReplaceEModifierSniff.
+ *
+ * @category  PHP
+ * @package   PHPCompatibility
+ * @author    Wim Godden <wim.godden@cu.be>
+ * @version   1.1.0
+ * @copyright 2014 Cu.be Solutions bvba
+ */
+class WordPress_Sniffs_PHPCompatibility_PregReplaceEModifierSniff extends WordPress_Sniffs_PHPCompatibility_Sniff
+{
+
+    /**
+     * If true, an error will be thrown; otherwise a warning.
+     *
+     * @var bool
+     */
+    public $error = false;
+
+    /**
+     * 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_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
+    {
+        if ($this->supportsAbove('5.5')) {
+            $tokens = $phpcsFile->getTokens();
+
+            if ($tokens[$stackPtr]['content'] == "preg_replace") {
+                $openBracket = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+
+                if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
+                    return;
+                }
+
+                $firstParam = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($openBracket + 1), null, true);
+
+                /**
+                 * If argument is not a string, then skip test (e.g. if variable passed in).
+                 */
+                if ($tokens[$firstParam]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
+                    return;
+                }
+
+                /**
+                 * Regex is a T_CONSTANT_ENCAPSED_STRING, so we need to remove the quotes
+                 */
+                $regex = "";
+                while (isset($tokens[$firstParam]) && $tokens[$firstParam]['code'] != T_COMMA) {
+                    if ($tokens[$firstParam]['code'] == T_CONSTANT_ENCAPSED_STRING) {
+                        $regex .= $tokens[$firstParam]['content'];
+                    }
+                    $firstParam++;
+                }
+
+                $doublesSeparators = array(
+                    '{' => '}',
+                );
+
+                $regex = substr($regex, 1, -1);
+
+                $regexFirstChar = substr($regex, 0, 1);
+                $regexEndPos = (array_key_exists($regexFirstChar, $doublesSeparators)) ?
+                                    strrpos($regex, $doublesSeparators[$regexFirstChar])
+                                    : strrpos($regex, $regexFirstChar);
+
+                if($regexEndPos) {
+                    $modifiers = substr($regex, $regexEndPos + 1);
+
+                    if (strpos($modifiers, "e") !== false) {
+                        if ($this->supportsAbove('7.0')) {
+                            $error = 'preg_replace() - /e modifier is forbidden in PHP 7.0';
+                        } else {
+                            $error = 'preg_replace() - /e modifier is deprecated in PHP 5.5';
+                        }
+                        $phpcsFile->addError($error, $stackPtr);
+                    }
+                }
+            }
+        }
+
+
+    }//end process()
+
+}//end class
diff --git a/WordPress/Sniffs/PHPCompatibility/README.md b/WordPress/Sniffs/PHPCompatibility/README.md
new file mode 100644
index 0000000000..4f240deb1e
--- /dev/null
+++ b/WordPress/Sniffs/PHPCompatibility/README.md
@@ -0,0 +1,24 @@
+### Important note about the PHPCompatibility Sniffs folder
+
+The sniffs in this folder come from another sniff library called [PHPCompatibility].
+Only sniffs which are one-on-one copies from sniffs in that library should be added here.
+
+To make copies of sniffs from [PHPCompatibility] work within the WordPress Coding Standards library, you need to make the following change:
+```php
+// Change the class line from:
+class PHPCompatibility_Sniffs_PHP_SniffNameSniff extends PHPCompatibility_Sniff
+
+// to:
+class WordPress_Sniffs_PHPCompatibility_SniffNameSniff extends WordPress_Sniffs_PHPCompatibility_Sniff
+```
+
+
+**_If these sniffs are adjusted - other than as outline above -, they should not be placed in this folder._**
+
+
+* Sniffs in this folder are excluded from the WPCS code style check to allow for copy/paste syncing of the files.
+* Sniffs in this folder generally do not have unit tests as they are unit tested in the sister-sniff-library [PHPCompatibility].
+* Any issues with these sniffs should be [reported](https://github.com/wimg/PHPCompatibility/issues/new) and solved in the sister-sniff-library [PHPCompatibility], not in WPCS.
+
+
+[PHPCompatibility]: https://github.com/wimg/PHPCompatibility
\ No newline at end of file
diff --git a/WordPress/Sniffs/PHPCompatibility/Sniff.php b/WordPress/Sniffs/PHPCompatibility/Sniff.php
new file mode 100644
index 0000000000..4e2d785671
--- /dev/null
+++ b/WordPress/Sniffs/PHPCompatibility/Sniff.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * PHPCompatibility_Sniff.
+ *
+ * PHP version 5.6
+ *
+ * @category  PHP
+ * @package   PHPCompatibility
+ * @author    Wim Godden <wim.godden@cu.be>
+ * @copyright 2014 Cu.be Solutions bvba
+ */
+
+/**
+ * PHPCompatibility_Sniff.
+ *
+ * @category  PHP
+ * @package   PHPCompatibility
+ * @author    Wim Godden <wim.godden@cu.be>
+ * @version   1.1.0
+ * @copyright 2014 Cu.be Solutions bvba
+ */
+abstract class WordPress_Sniffs_PHPCompatibility_Sniff implements PHP_CodeSniffer_Sniff
+{
+
+/* The testVersion configuration variable may be in any of the following formats:
+ * 1) Omitted/empty, in which case no version is specified.  This effectively
+ *    disables all the checks provided by this standard.
+ * 2) A single PHP version number, e.g. "5.4" in which case the standard checks that
+ *    the code will run on that version of PHP (no deprecated features or newer
+ *    features being used).
+ * 3) A range, e.g. "5.0-5.5", in which case the standard checks the code will run
+ *    on all PHP versions in that range, and that it doesn't use any features that
+ *    were deprecated by the final version in the list, or which were not available
+ *    for the first version in the list.
+ * PHP version numbers should always be in Major.Minor format.  Both "5", "5.3.2"
+ * would be treated as invalid, and ignored.
+ * This standard doesn't support checking against PHP4, so the minimum version that
+ * is recognised is "5.0".
+ */
+
+    private function getTestVersion()
+    {
+        /**
+         * var $testVersion will hold an array containing min/max version of PHP
+         *   that we are checking against (see above).  If only a single version
+         *   number is specified, then this is used as both the min and max.
+         */
+        static $arrTestVersions;
+
+        if (!isset($testVersion)) {
+            $testVersion = PHP_CodeSniffer::getConfigData('testVersion');
+            $testVersion = trim($testVersion);
+
+            $arrTestVersions = array(null, null);
+            if (preg_match('/^\d+\.\d+$/', $testVersion)) {
+                $arrTestVersions = array($testVersion, $testVersion);
+            }
+            elseif (preg_match('/^(\d+\.\d+)\s*-\s*(\d+\.\d+)$/', $testVersion,
+                               $matches))
+            {
+                if (version_compare($matches[1], $matches[2], ">")) {
+                    trigger_error("Invalid range in testVersion setting: '"
+                                  . $testVersion . "'", E_USER_WARNING);
+                }
+                else {
+                    $arrTestVersions = array($matches[1], $matches[2]);
+                }
+            }
+            elseif (!$testVersion == "") {
+                trigger_error("Invalid testVersion setting: '" . $testVersion
+                              . "'", E_USER_WARNING);
+            }
+        }
+
+        return $arrTestVersions;
+    }
+
+    public function supportsAbove($phpVersion)
+    {
+        $testVersion = $this->getTestVersion();
+        $testVersion = $testVersion[1];
+
+        if (is_null($testVersion)
+            || version_compare($testVersion, $phpVersion) >= 0
+        ) {
+            return true;
+        } else {
+            return false;
+        }
+    }//end supportsAbove()
+
+    public function supportsBelow($phpVersion)
+    {
+        $testVersion = $this->getTestVersion();
+        $testVersion = $testVersion[0];
+
+        if (!is_null($testVersion)
+            && version_compare($testVersion, $phpVersion) <= 0
+        ) {
+            return true;
+        } else {
+            return false;
+        }
+    }//end supportsBelow()
+
+}//end class
diff --git a/bin/phpcs.xml b/bin/phpcs.xml
index aa8ec9d7fc..efe80acdd5 100644
--- a/bin/phpcs.xml
+++ b/bin/phpcs.xml
@@ -2,6 +2,8 @@
 <ruleset name="WordPress Coding Standards">
 	<description>The Coding standard for the WordPress Coding Standards itself.</description>
 
+	<exclude-pattern>*/Sniffs/PHPCompatibility/*</exclude-pattern>
+
 	<rule ref="WordPress-Core">
 		<exclude name="Generic.Files.LowercasedFilename" />
 		<exclude name="WordPress.NamingConventions.ValidVariableName" />