Permalink
Browse files

escape raw ampersands before passing to addChild.

For more info, see note at line 109 of services/twilio/twiml.php
  • Loading branch information...
1 parent d2b2ca3 commit 1ae9414f2cd2e9d2833a62b16bbd96594617981e @kevinburke kevinburke committed Nov 2, 2011
Showing with 45 additions and 4 deletions.
  1. +30 −1 Services/Twilio/Twiml.php
  2. +2 −2 docs/usage/twiml.rst
  3. +3 −0 tests/README
  4. +10 −1 tests/TwimlTest.php
View
31 Services/Twilio/Twiml.php
@@ -79,10 +79,39 @@ public function __call($verb, array $args)
if (is_array($noun)) {
list($attrs, $noun) = array($noun, '');
}
+ /* addChild does not escape XML, while addAttribute does. This means if
+ * you pass unescaped ampersands ("&") to addChild, you will generate
+ * an error.
+ *
+ * Some inexperienced developers will pass in unescaped ampersands, and
+ * we want to make their code work, by escaping the ampersands for them
+ * before passing the string to addChild. (with htmlentities)
+ *
+ * However other people will know what to do, and their code
+ * already escapes ampersands before passing them to addChild. We don't
+ * want to break their existing code by turning their &'s into
+ * &
+ *
+ * So we end up with the following matrix:
+ *
+ * We want & to turn into & before passing to addChild
+ * We want & to stay as & before passing to addChild
+ *
+ * The following line accomplishes the desired behavior.
+ */
+ $normalized = htmlentities(html_entity_decode($noun));
+ //then escape it again
$child = empty($noun)
? $this->element->addChild(ucfirst($verb))
- : $this->element->addChild(ucfirst($verb), $noun);
+ : $this->element->addChild(ucfirst($verb), $normalized);
foreach ($attrs as $name => $value) {
+ /* Note that addAttribute escapes raw ampersands by default, so we
+ * haven't touched its implementation. So this is the matrix for
+ * addAttribute:
+ *
+ * & turns into &
+ * & turns into &
+ */
$child->addAttribute($name, $value);
}
return new self($child);
View
4 docs/usage/twiml.rst
@@ -218,14 +218,14 @@ Redirect
.. code-block:: php
$response = new Services_Twilio_Twiml;
- $response->redirect('http://foo.com/path/to/resource');
+ $response->redirect('http://twimlets.com/voicemail?Email=somebody@somedomain.com');
print $response;
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
- <Redirect>http://foo.com/path/to/resource</Redirect>
+ <Redirect>http://twimlets.com/voicemail?Email=somebody@somedomain.com</Redirect>
</Response>
View
3 tests/README
@@ -0,0 +1,3 @@
+# To run the tests, navigate to the twilio-php home directory, then run:
+
+make test
View
11 tests/TwimlTest.php
@@ -117,7 +117,16 @@ public function testRedirect() {
$expected = '<Response><Redirect></Redirect></Response>';
$this->assertXmlStringEqualsXmlString($expected, $r);
}
-
+
+ public function testAmpersandEscaping() {
+ $r = new Services_Twilio_Twiml();
+ $test_amp = "test&two&amp;three";
+ $r->redirect($test_amp);
+ $expected = '<Response><Redirect>' .
+ 'test&amp;two&amp;three</Redirect></Response>';
+ $this->assertXmlStringEqualsXmlString($expected, $r);
+ }
+
public function testRedirectConvience() {
$r = new Services_Twilio_Twiml();
$r->redirect();

0 comments on commit 1ae9414

Please sign in to comment.