Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>If you need to do the exact escape sequences like PHP does, you need the long version, which is the <code>DoubleQuoted</code> class. I extended input string a bit to cover more escape sequences than in your question to make this more generic:</p> <pre><code>$content = '\\\\t\tThis variable\\string is\x20not\40set by me.\nCannot \do anything about it.\n'; $dq = new DoubleQuoted($content); echo $dq; </code></pre> <p>Output:</p> <pre><code>\\t This variable\string is not set by me. Cannot \do anything about it. </code></pre> <p>However, if you're okay to come closely to that, there is a PHP function called <a href="http://php.net/stripcslashes" rel="nofollow noreferrer"><code>stripcslashes</code></a>, for comparison, I've added the result of it and the PHP double-quote string:</p> <pre><code>echo stripcslashes($content), "\n"; $compare = "\\\\t\tThis variable\\string is\x20not\40set by me.\nCannot \do anything about it.\n"; echo $compare, "\n"; </code></pre> <p>Output:</p> <pre><code>\t This variablestring is not set by me. Cannot do anything about it. \\t This variable\string is not set by me. Cannot \do anything about it. </code></pre> <p>As you can see <code>stripcslashes</code> drops some characters here compared to PHP native output.</p> <p>(<strong>Edit:</strong> <a href="https://stackoverflow.com/a/8314506/367456">See my other answer as well</a> which offers something simple and sweet with <code>cstripslashes</code> and <code>preg_replace</code>.)</p> <p>If <code>stripcslashes</code> is not suitable, there is <code>DoubleQuoted</code>. It's constructor takes a string that is treated like a double quoted string (minus variable substitution, only the character escape sequences).</p> <p>As the manual outlines, there are multiple escape sequences. They look like regular expressions, and all start with <code>\</code>, so it's looks near to actually use regular expressions to replace them.</p> <p>However there is one exception: <code>\\</code> will skip the escape sequence. The regular expression would need to have backtracking and/or atomic groups to deal with that and I'm not fluent with those so I just did a simple trick: I only applied the regular expressions to those parts of the string which do not contain <code>\\</code> by simply exploding the string first and then imploding it again.</p> <p>The two regular expression based replace functions, <a href="http://php.net/preg_replace" rel="nofollow noreferrer"><code>preg_replace</code><sup><em>Doc</em></sup></a> and <a href="http://php.net/preg_replace_callback" rel="nofollow noreferrer"><code>preg_replace_callback</code><sup><em>Doc</em></sup></a>, allow to operate on arrays as well, so this is quite easy to do.</p> <p>It's done in the <a href="http://php.net/__toString" rel="nofollow noreferrer"><code>__toString()</code><sup><em>Doc</em></sup></a> function:</p> <pre><code>class DoubleQuoted { ... private $string; public function __construct($string) { $this-&gt;string = $string; } ... public function __toString() { $this-&gt;exception = NULL; $patterns = $this-&gt;getPatterns(); $callback = $this-&gt;getCallback(); $parts = explode('\\\\', $this-&gt;string); try { $parts = preg_replace_callback($patterns, $callback, $parts); } catch(Exception $e) { $this-&gt;exception = $e; return FALSE; # provoke exception } return implode('\\\\', $parts); } ... </code></pre> <p>See the <a href="http://php.net/explode" rel="nofollow noreferrer"><code>explode</code><sup><em>Doc</em></sup></a> and <a href="http://php.net/implode" rel="nofollow noreferrer"><code>implode</code><sup><em>Doc</em></sup></a> calls. Those take care that <code>preg_replace_callback</code> does not operate on any string that contains <code>\\</code>. So the replace operation has been freed from the burden to deal with these special cases. This is the callback function which is invoked by <code>preg_replace_callback</code> for each pattern match. I wrapped it into a closure so it is not publicly accessible:</p> <pre><code>private function getCallback() { $map = $this-&gt;map; return function($matches) use ($map) { list($full, $type, $number) = $matches += array('', NULL, NULL); if (NULL === $type) throw new UnexpectedValueException(sprintf('Match was %s', $full)) ; if (NULL === $number) return isset($map[$type]) ? $map[$type] : '\\'.$type ; switch($type) { case 'x': return chr(hexdec($number)); case '': return chr(octdec($number)); default: throw new UnexpectedValueException(sprintf('Match was %s', $full)); } }; } </code></pre> <p>You need some additional information to understand it as this is not the complete class already. I go through the missing points and add the missing code as well:</p> <p>All patterns the class "looks for" contain subgroups, at least one. That one goes into <code>$type</code> and is either the single character to be translated <em>or</em> an empty string for octals and an <code>x</code> for hexadecimal numbers.</p> <p>The optional second group <code>$number</code> is either not set (<code>NULL</code>) or contains the octal/hexadecimal number. The <code>$matches</code> input is normalized to the just named variables in this line:</p> <pre><code>list($full, $type, $number) = $matches += array('', NULL, NULL); </code></pre> <p>Patterns are defined upfront as sequences in a private member variable:</p> <pre><code>private $sequences = array( '(n|r|t|v|f|\\$|")', # single escape characters '()([0-7]{1,3})', # octal '(x)([0-9A-Fa-f]{1,2})', # hex ); </code></pre> <p>The <code>getPatterns()</code> function just wraps those definitions into valid PCRE regular expressions like:</p> <pre><code>/\\(n|r|t|v|f|\$|")/ # single escape characters /\\()([0-7]{1,3})/ # octal /\\(x)([0-9A-Fa-f]{1,2})/ # hex </code></pre> <p>It is pretty simple:</p> <pre><code>private function getPatterns() { foreach($this-&gt;sequences as $sequence) $patterns[] = sprintf('/\\\\%s/', $sequence) ; return $patterns; } </code></pre> <p>Now as the patterns are outlined, this explains what <code>$matches</code> contain when the callback function is invoked.</p> <p>The other thing you need to know to understand how the callback works is <code>$map</code>. That's just an array containing the single replacement characters:</p> <pre><code>private $map = array( 'n' =&gt; "\n", 'r' =&gt; "\r", 't' =&gt; "\t", 'v' =&gt; "\v", 'f' =&gt; "\f", '$' =&gt; '$', '"' =&gt; '"', ); </code></pre> <p>And that's already pretty much it for the class. There is another private variable <code>$this-&gt;exception</code> that is used to store if an exception has been thrown as <code>__toString()</code> can not throw exceptions and would lead to a fatal error if it would happen in the callback function. So it's caught and stored to a private class variable, here again that part of the code:</p> <pre><code> ... public function __toString() { $this-&gt;exception = NULL; ... try { $parts = preg_replace_callback($patterns, $callback, $parts); } catch(Exception $e) { $this-&gt;exception = $e; return FALSE; # provoke exception } ... </code></pre> <p>In case of an exception while replacing, the function exists with <code>FALSE</code> which will lead to a catchable exception. A getter function makes the internal exception available then:</p> <pre><code>private $exception; ... public function getException() { return $this-&gt;exception; } </code></pre> <p>As it's nice to access the original string as well, you can add another getter to obtain that:</p> <pre><code>public function getString() { return $this-&gt;string; } </code></pre> <p>And that's the whole class. Hope this is helpful.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload