1 : <?php
2 :
3 : /**
4 : * Exception class for Services_Twilio_Twiml.
5 : */
6 : class Services_Twilio_TwimlException extends Exception {}
7 :
8 : /**
9 : * Twiml response generator.
10 : *
11 : * @category Services
12 : * @package Services_Twilio
13 : * @author Neuman Vong <neuman at ashmoremusic dot com>
14 : * @license http://creativecommons.org/licenses/MIT/ MIT
15 : * @link https://gist.github.com/855985
16 : */
17 : class Services_Twilio_Twiml
18 : {
19 :
20 : protected $element;
21 :
22 : /**
23 : * Constructs a Twiml response.
24 : *
25 : * @param SimpleXmlElement|array $arg:
26 : * - the element to wrap
27 : * - attributes to add to the element
28 : * - if null, initialize an empty element named 'Response'
29 : */
30 : public function __construct($arg = null)
31 : {
32 : switch (true) {
33 41 : case $arg instanceof SimpleXmlElement:
34 40 : $this->element = $arg;
35 40 : break;
36 41 : case $arg === null:
37 41 : $this->element = new SimpleXmlElement('<Response/>');
38 41 : break;
39 0 : case is_array($arg):
40 0 : $this->element = new SimpleXmlElement('<Response/>');
41 0 : foreach ($arg as $name => $value) {
42 0 : $this->element->addAttribute($name, $value);
43 0 : }
44 0 : break;
45 0 : default:
46 0 : throw new TwimlException('Invalid argument');
47 0 : }
48 41 : }
49 :
50 : /**
51 : * Converts method calls into Twiml verbs.
52 : *
53 : * A basic example:
54 : *
55 : * php> print $this->say('hello');
56 : * <Say>hello</Say>
57 : *
58 : * An example with attributes:
59 : *
60 : * php> print $this->say('hello', array('voice' => 'woman'));
61 : * <Say voice="woman">hello</Say>
62 : *
63 : * You could even just pass in an attributes array, omitting the noun:
64 : *
65 : * php> print $this->gather(array('timeout' => '20'));
66 : * <Gather timeout="20"/>
67 : *
68 : * @param string $verb The Twiml verb.
69 : * @param array $args:
70 : * - (noun string)
71 : * - (noun string, attributes array)
72 : * - (attributes array)
73 : *
74 : * @return SimpleXmlElement A SimpleXmlElement
75 : */
76 : public function __call($verb, array $args)
77 : {
78 40 : list($noun, $attrs) = $args + array('', array());
79 40 : if (is_array($noun)) {
80 16 : list($attrs, $noun) = array($noun, '');
81 16 : }
82 : /* addChild does not escape XML, while addAttribute does. This means if
83 : * you pass unescaped ampersands ("&") to addChild, you will generate
84 : * an error.
85 : *
86 : * Some inexperienced developers will pass in unescaped ampersands, and
87 : * we want to make their code work, by escaping the ampersands for them
88 : * before passing the string to addChild. (with htmlentities)
89 : *
90 : * However other people will know what to do, and their code
91 : * already escapes ampersands before passing them to addChild. We don't
92 : * want to break their existing code by turning their &'s into
93 : * &amp;
94 : *
95 : * So we end up with the following matrix:
96 : *
97 : * We want & to turn into & before passing to addChild
98 : * We want & to stay as & before passing to addChild
99 : *
100 : * The following line accomplishes the desired behavior.
101 : */
102 40 : $normalized = htmlentities($noun, null, null, false);
103 : //then escape it again
104 40 : $child = empty($noun)
105 40 : ? $this->element->addChild(ucfirst($verb))
106 40 : : $this->element->addChild(ucfirst($verb), $normalized);
107 40 : foreach ($attrs as $name => $value) {
108 : /* Note that addAttribute escapes raw ampersands by default, so we
109 : * haven't touched its implementation. So this is the matrix for
110 : * addAttribute:
111 : *
112 : * & turns into &
113 : * & turns into &amp;
114 : */
115 22 : if (is_bool($value)) {
116 1 : $value = ($value === true) ? 'true' : 'false';
117 1 : }
118 22 : $child->addAttribute($name, $value);
119 40 : }
120 40 : return new self($child);
121 : }
122 :
123 : /**
124 : * Returns the object as XML.
125 : *
126 : * @return string The response as an XML string
127 : */
128 : public function __toString()
129 : {
130 41 : $xml = $this->element->asXml();
131 41 : return str_replace(
132 41 : '<?xml version="1.0"?>',
133 41 : '<?xml version="1.0" encoding="UTF-8"?>', $xml);
134 : }
135 : }
|