Skip to content

Commit 541a59a

Browse files
Jon WenmothJon Wenmoth
Jon Wenmoth
authored and
Jon Wenmoth
committed
Initial import
1 parent 1da3d04 commit 541a59a

14 files changed

+925
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.DS_Store

bin/phantomjs

9.15 MB
Binary file not shown.

composer.json

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "jonnyw/php-phantomjs",
3+
"description": "Execute PhantomJS commands through PHP",
4+
"authors": [
5+
{
6+
"name": "Jonny Wenmoth",
7+
"email": "[email protected]"
8+
}
9+
],
10+
"minimum-stability": "stable",
11+
"require": {
12+
"php": ">=5.3.0"
13+
},
14+
"autoload": {
15+
"psr-0": {
16+
"JonnyW\\PhantomJs\\": "src"
17+
},
18+
"classmap": ["src/"]
19+
},
20+
"bin": [
21+
"bin/phantomjs"
22+
]
23+
}

phpunit.xml.dist

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<phpunit bootstrap="./test/bootstrap.php" colors="true">
4+
<testsuites>
5+
<testsuite name="PhantomJS Test Suite">
6+
<directory suffix="Test.php">./test/</directory>
7+
</testsuite>
8+
</testsuites>
9+
<filter>
10+
<whitelist>
11+
<directory suffix=".php">./src/</directory>
12+
</whitelist>
13+
</filter>
14+
</phpunit>

src/JonnyW/PhantomJs/Client.php

+299
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
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+
}
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
/**
12+
* PHP PhantomJs
13+
*
14+
* @author Jon Wenmoth <[email protected]>
15+
*/
16+
interface ClientInterface
17+
{
18+
19+
/**
20+
* Open page and return HTML
21+
*
22+
* @param string $url
23+
* @return string
24+
*/
25+
public function open($url);
26+
27+
/**
28+
* Screen capture URL
29+
*
30+
* @param string $url
31+
* @pram string $file
32+
* @return string
33+
*/
34+
public function capture($url, $file);
35+
36+
/**
37+
* Set new PhantomJs path
38+
*
39+
* @param string $path
40+
* @return JonnyW\PhantomJs\ClientInterface
41+
*/
42+
public function setPhantomJs($path);
43+
}

0 commit comments

Comments
 (0)