1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the php-phantomjs.
5
+ *
6
+ * For the full copyright and license information, please view the LICENSE
7
+ * file that was distributed with this source code.
8
+ */
9
+ namespace JonnyW \PhantomJs ;
10
+
11
+ use JonnyW \PhantomJs \ClientInterface ;
12
+ use JonnyW \PhantomJs \Exception \NoPhantomJsException ;
13
+ use JonnyW \PhantomJs \Exception \CommandFailedException ;
14
+ use JonnyW \PhantomJs \Exception \NotWriteableException ;
15
+ use JonnyW \PhantomJs \Exception \InvalidUrlException ;
16
+ use JonnyW \PhantomJs \Response ;
17
+
18
+ /**
19
+ * PHP PhantomJs
20
+ *
21
+ * @author Jon Wenmoth <[email protected] >
22
+ */
23
+ class Client implements ClientInterface
24
+ {
25
+ /**
26
+ * Path to phantomJS executable
27
+ *
28
+ * @var string
29
+ */
30
+ protected $ phantomJS ;
31
+
32
+ /**
33
+ * Client instance
34
+ *
35
+ * @var JonnyW\PhantomJs\ClientInterface
36
+ */
37
+ private static $ instance ;
38
+
39
+ /**
40
+ * Internal constructor
41
+ *
42
+ * @return void
43
+ */
44
+ public function __construct ()
45
+ {
46
+ $ this ->phantomJS = 'bin/phantomjs ' ;
47
+ $ this ->timeout = 5000 ;
48
+ }
49
+
50
+ /**
51
+ * Get singleton instance
52
+ *
53
+ * @return JonnyW\PhantomJs\ClientInterface
54
+ */
55
+ public static function getInstance ()
56
+ {
57
+ if (!self ::$ instance instanceof ClientInterface) {
58
+ self ::$ instance = new Client ();
59
+ }
60
+
61
+ return self ::$ instance ;
62
+ }
63
+
64
+ /**
65
+ * Open page and return HTML
66
+ *
67
+ * @param string $url
68
+ * @return string
69
+ */
70
+ public function open ($ url )
71
+ {
72
+ return $ this ->request ($ url , $ this ->openCmd );
73
+ }
74
+
75
+ /**
76
+ * Screen capture URL
77
+ *
78
+ * @param string $url
79
+ * @pram string $file
80
+ * @return string
81
+ */
82
+ public function capture ($ url , $ file )
83
+ {
84
+ if (!is_writable (dirname ($ file ))) {
85
+ throw new NotWriteableException (sprintf ('Path is not writeable by PhantomJs: %s ' , $ file ));
86
+ }
87
+
88
+ $ cmd = sprintf ($ this ->captureCmd , $ file );
89
+
90
+ return $ this ->request ($ url , $ cmd );
91
+ }
92
+
93
+ /**
94
+ * Set new PhantomJs path
95
+ *
96
+ * @param string $path
97
+ * @return JonnyW\PhantomJs\ClientInterface
98
+ */
99
+ public function setPhantomJs ($ path )
100
+ {
101
+ if (!file_exists ($ path ) || !is_executable ($ path )) {
102
+ throw new NoPhantomJsException (sprintf ('PhantomJs file does not exist or is not executable: %s ' , $ path ));
103
+ }
104
+
105
+ $ this ->phantomJS = $ path ;
106
+
107
+ return $ this ;
108
+ }
109
+
110
+ /**
111
+ * Set timeout period (in milliseconds)
112
+ *
113
+ * @param int $period
114
+ * @return JonnyW\PhantomJs\ClientInterface
115
+ */
116
+ public function setTimeout ($ period )
117
+ {
118
+ $ this ->timeout = $ period ;
119
+
120
+ return $ this ;
121
+ }
122
+
123
+ /**
124
+ * Call PhantomJs command
125
+ *
126
+ * @param string $url
127
+ * @param string $cmd
128
+ * @return JonnyW\PhantomJs\Response
129
+ */
130
+ protected function request ($ url , $ cmd )
131
+ {
132
+ // Validate URL
133
+ if (!filter_var ($ url , FILTER_VALIDATE_URL , FILTER_FLAG_HOST_REQUIRED )) {
134
+ throw new InvalidUrlException (sprintf ('Invalid URL provided: %s ' , $ url ));
135
+ }
136
+
137
+ // Validate PhantomJS executable
138
+ if (!file_exists ($ this ->phantomJS ) || !is_executable ($ this ->phantomJS )) {
139
+ throw new NoPhantomJsException (sprintf ('PhantomJs file does not exist or is not executable: %s ' , $ this ->phantomJS ));
140
+ }
141
+
142
+ try {
143
+
144
+ $ script = false ;
145
+
146
+ $ data = sprintf (
147
+ $ this ->wrapper ,
148
+ $ this ->timeout ,
149
+ $ url ,
150
+ $ cmd
151
+ );
152
+
153
+ $ script = $ this ->writeScript ($ data );
154
+ $ cmd = escapeshellcmd (sprintf ("%s %s " , $ this ->phantomJS , $ script ));
155
+
156
+ $ data = shell_exec ($ cmd );
157
+ $ data = $ this ->parse ($ data );
158
+
159
+ $ this ->removeScript ($ script );
160
+
161
+ $ response = new Response ($ data );
162
+ }
163
+ catch (NotWriteableException $ e ) {
164
+ throw $ e ;
165
+ }
166
+ catch (\Exception $ e ) {
167
+
168
+ $ this ->removeScript ($ script );
169
+
170
+ throw new CommandFailedException (sprintf ('Error when executing PhantomJs command: %s - %s ' , $ cmd , $ e ->getMessage ()));
171
+ }
172
+
173
+ return $ response ;
174
+ }
175
+
176
+ /**
177
+ * Write temporary script file and
178
+ * return path to file
179
+ *
180
+ * @param string $data
181
+ * @return JonnyW\PhantomJs\ClientInterface
182
+ */
183
+ protected function writeScript ($ data )
184
+ {
185
+ $ file = tempnam ('/tmp ' , 'phantomjs ' );
186
+
187
+ // Could not create tmp file
188
+ if (!$ file || !is_writable ($ file )) {
189
+ throw new NotWriteableException ('Could not create tmp file on system. Please check your tmp directory and make sure it is writeable. ' );
190
+ }
191
+
192
+ // Could not write script data to tmp file
193
+ if (file_put_contents ($ file , $ data ) === false ) {
194
+
195
+ $ this ->removeScript ($ file );
196
+
197
+ throw new NotWriteableException (sprintf ('Could not write data to tmp file: %s. Please check your tmp directory and make sure it is writeable. ' , $ file ));
198
+ }
199
+
200
+ return $ file ;
201
+ }
202
+
203
+ /**
204
+ * Remove temporary script file
205
+ *
206
+ * @param string $file
207
+ * @return JonnyW\PhantomJs\ClientInterface
208
+ */
209
+ protected function removeScript ($ file )
210
+ {
211
+ if ($ file && file_exists ($ file )) {
212
+ unlink ($ file );
213
+ }
214
+
215
+ return $ this ;
216
+ }
217
+
218
+ /**
219
+ * If data from JSON string format
220
+ * and return array
221
+ *
222
+ * @param string $data
223
+ * @return array
224
+ */
225
+ protected function parse ($ data )
226
+ {
227
+ // Data is invalid
228
+ if ($ data === null || !is_string ($ data )) {
229
+ return array ();
230
+ }
231
+
232
+ // Not a JSON string
233
+ if (substr ($ data , 0 , 1 ) !== '{ ' ) {
234
+ return array ();
235
+ }
236
+
237
+ // Return decoded JSON string
238
+ return (array ) json_decode ($ data , true );
239
+ }
240
+
241
+ /**
242
+ * PhantomJs base wrapper
243
+ *
244
+ * @var string
245
+ */
246
+ protected $ wrapper = <<<EOF
247
+
248
+ var page = require('webpage').create(),
249
+ response = {};
250
+
251
+ page.settings.resourceTimeout = %1 \$s;
252
+ page.onResourceTimeout = function(e) {
253
+ response = e;
254
+ response.status = e.errorCode;
255
+ };
256
+
257
+ page.onResourceReceived = function (r) {
258
+ if(!response.status) response = r;
259
+ };
260
+
261
+ page.open('%2 \$s', function (status) {
262
+
263
+ if(status === 'success') {
264
+ %3 \$s
265
+ }
266
+
267
+ console.log(JSON.stringify(response, undefined, 4));
268
+ phantom.exit();
269
+ });
270
+ EOF ;
271
+
272
+ /**
273
+ * PhantomJs screen capture
274
+ * command template
275
+ *
276
+ * @var string
277
+ */
278
+ protected $ captureCmd = <<<EOF
279
+
280
+ page.render('%1 \$s');
281
+
282
+ response.content = page.evaluate(function () {
283
+ return document.getElementsByTagName('html')[0].innerHTML
284
+ });
285
+ EOF ;
286
+
287
+ /**
288
+ * PhantomJs page open
289
+ * command template
290
+ *
291
+ * @var string
292
+ */
293
+ protected $ openCmd = <<<EOF
294
+
295
+ response.content = page.evaluate(function () {
296
+ return document.getElementsByTagName('html')[0].innerHTML
297
+ });
298
+ EOF ;
299
+ }
0 commit comments