├── .github
└── release-drafter.yml
├── .gitignore
├── LICENSE.md
├── README.md
└── phppsinfo.php
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | branches:
2 | - master
3 | name-template: v$NEXT_PATCH_VERSION
4 | tag-template: v$NEXT_PATCH_VERSION
5 | categories:
6 | - title: 🚀 Improvements
7 | label: enhancement
8 | - title: 🐛 Bug Fixes
9 | label: bug
10 | change-template: '- #$NUMBER: $TITLE (by @$AUTHOR)'
11 | template: |
12 | # Changes
13 | $CHANGES
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .project\n
2 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 PrestaShop
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PhpPsInfo - PrestaShop system requirements checker
2 |
3 | 
4 |
5 | PhpPsInfo is a PHP script that allows you to test if your current environment fullfills PrestaShop's requirements, and offers suggestions for improvements.
6 |
7 | This script allows you to quickly test the environement where you want to install PrestaShop.
8 |
9 | The default credentials for displaying the results are:
10 |
11 | * Login: prestashop
12 | * Password: prestashop
13 |
14 | You can set server env vars to override them:
15 |
16 | * `PS_INFO_LOGIN`
17 | * `PS_INFO_PASSWORD`
18 |
19 |
20 | Tests include the following:
21 |
22 | * PHP & MySQL Version
23 | * Apache modules
24 | * PHP Extensions
25 | * PHP Configuration
26 | * MySQL Configuration
27 | * Directories Configuration
28 |
29 | ## Usage
30 |
31 | 1. Download the [latest release](https://github.com/PrestaShop/php-ps-info/releases).
32 | 2. Extract the zip file.
33 | 3. Upload the `phppsinfo.php` file to your server and put it inside your current shop’s directory or the one where you intend to install it.
34 | 4. Open it up on your browser (`http://your-domain.com/path-to-your-prestashop/phppsinfo.php`).
35 | 5. Type in the login and password if prompted (use `prestashop` for both).
36 | 6. Enjoy!
37 |
38 | ## License
39 |
40 | This tool is released under the MIT License.
41 |
42 | ## Troubleshooting
43 |
44 | Working with a CGI environment such as Apache + FPM, you have to add a custom Apache RewriteCond.
45 |
46 | ```
47 | RewriteCond %{HTTP:Authorization} .
48 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
49 | ```
50 |
--------------------------------------------------------------------------------
/phppsinfo.php:
--------------------------------------------------------------------------------
1 | [
22 | 'php' => '7.1',
23 | 'mysql' => '5.5',
24 | ],
25 | 'extensions' => [
26 | 'bcmath' => false,
27 | 'curl' => true,
28 | 'dom' => true,
29 | 'fileinfo' => true,
30 | 'gd' => true,
31 | 'iconv' => true,
32 | 'imagick' => false,
33 | 'intl' => true,
34 | 'json' => true,
35 | 'mbstring' => true,
36 | 'memcache' => false,
37 | 'memcached' => false,
38 | 'openssl' => true,
39 | 'pdo_mysql' => true,
40 | 'simplexml' => true,
41 | 'zip' => true,
42 | ],
43 | 'config' => [
44 | 'allow_url_fopen' => true,
45 | 'expose_php' => false,
46 | 'file_uploads' => true,
47 | 'max_input_vars' => 1000,
48 | 'memory_limit' => '64M',
49 | 'post_max_size' => '16M',
50 | 'register_argc_argv' => false,
51 | 'set_time_limit' => true,
52 | 'short_open_tag' => false,
53 | 'upload_max_filesize' => '4M',
54 | ],
55 | 'directories' => [
56 | 'cache_dir' => 'var/cache',
57 | 'log_dir' => 'var/logs',
58 | 'img_dir' => 'img',
59 | 'mails_dir' => 'mails',
60 | 'module_dir' => 'modules',
61 | 'translations_dir' => 'translations',
62 | 'customizable_products_dir' => 'upload',
63 | 'virtual_products_dir' => 'download',
64 | 'override_dir' => 'override',
65 | 'config_sf2_dir' => 'app/config',
66 | 'translations_sf2' => 'app/Resources/translations',
67 | ],
68 | 'apache_modules' => [
69 | 'mod_alias' => false,
70 | 'mod_env' => false,
71 | 'mod_headers' => false,
72 | 'mod_rewrite' => false,
73 | ],
74 | ];
75 |
76 | protected $recommended = [
77 | 'versions' => [
78 | 'php' => '8.1',
79 | 'mysql' => '8.0',
80 | ],
81 | 'extensions' => [
82 | 'bcmath' => true,
83 | 'curl' => true,
84 | 'dom' => true,
85 | 'fileinfo' => true,
86 | 'gd' => true,
87 | 'iconv' => true,
88 | 'imagick' => true,
89 | 'intl' => true,
90 | 'json' => true,
91 | 'mbstring' => true,
92 | 'memcache' => false,
93 | 'memcached' => true,
94 | 'openssl' => true,
95 | 'pdo_mysql' => true,
96 | 'simplexml' => true,
97 | 'zip' => true,
98 | ],
99 | 'config' => [
100 | 'allow_url_fopen' => true,
101 | 'expose_php' => false,
102 | 'file_uploads' => true,
103 | 'max_input_vars' => 5000,
104 | 'memory_limit' => '256M',
105 | 'post_max_size' => '128M',
106 | 'register_argc_argv' => false,
107 | 'set_time_limit' => true,
108 | 'short_open_tag' => false,
109 | 'upload_max_filesize' => '128M',
110 | ],
111 | 'apache_modules' => [
112 | 'mod_alias' => false,
113 | 'mod_env' => true,
114 | 'mod_headers' => true,
115 | 'mod_rewrite' => true,
116 | ],
117 | ];
118 |
119 | /**
120 | * Set up login and password with parameter or
121 | * you can set server env vars:
122 | * - PS_INFO_LOGIN
123 | * - PS_INFO_PASSWORD
124 | *
125 | * @param string $login Login
126 | * @param string $password Password
127 | *
128 | */
129 | public function __construct($login = self::DEFAULT_LOGIN, $password = self::DEFAULT_PASSWORD)
130 | {
131 | if (!empty($_SERVER['PS_INFO_LOGIN'])) {
132 | $this->login = $_SERVER['PS_INFO_LOGIN'];
133 | }
134 |
135 | if (!empty($_SERVER['PS_INFO_PASSWORD'])) {
136 | $this->password = $_SERVER['PS_INFO_PASSWORD'];
137 | }
138 |
139 | $this->login = !empty($login) ? $login : $this->login;
140 | $this->password = !empty($password) ? $password : $this->password;
141 | }
142 |
143 | /**
144 | * Check authentication if not in cli and have a login
145 | */
146 | public function checkAuth()
147 | {
148 | if (PHP_SAPI === 'cli' ||
149 | empty($this->login)
150 | ) {
151 | return;
152 | }
153 |
154 | if (!isset($_SERVER['PHP_AUTH_USER']) ||
155 | $_SERVER['PHP_AUTH_PW'] != $this->password ||
156 | $_SERVER['PHP_AUTH_USER'] != $this->login
157 | ) {
158 | header('WWW-Authenticate: Basic realm="Authentification"');
159 | header('HTTP/1.0 401 Unauthorized');
160 | echo '401 Unauthorized';
161 | exit(401);
162 | }
163 | }
164 |
165 | /**
166 | * Get versions data
167 | *
168 | * @return array
169 | */
170 | public function getVersions()
171 | {
172 | $data = [
173 | 'Web server' => [$this->getWebServer()],
174 | 'PHP Type' => [
175 | strpos(PHP_SAPI, 'cgi') !== false ?
176 | 'CGI with Apache Worker or another webserver' :
177 | (strpos(PHP_SAPI, 'litespeed') !== false ? 'Litespeed (Better performance)' : 'Apache Module (low performance)')
178 | ],
179 | ];
180 |
181 | $data['PHP Version'] = [
182 | $this->requirements['versions']['php'],
183 | $this->recommended['versions']['php'],
184 | PHP_VERSION,
185 | version_compare(PHP_VERSION, $this->recommended['versions']['php'], '>=') ?
186 | self::TYPE_OK : (
187 | version_compare(PHP_VERSION, $this->requirements['versions']['php'], '>=') ?
188 | self::TYPE_WARNING :
189 | self::TYPE_ERROR
190 | )
191 | ];
192 |
193 | if (!extension_loaded('mysqli') || !is_callable('mysqli_connect')) {
194 | $data['MySQLi Extension'] = [
195 | true,
196 | true,
197 | 'Not installed',
198 | self::TYPE_ERROR,
199 | ];
200 | } else {
201 | $data['MySQLi Extension'] = [
202 | $this->requirements['versions']['mysql'],
203 | $this->recommended['versions']['mysql'],
204 | mysqli_get_client_info(),
205 | self::TYPE_OK,
206 | ];
207 | }
208 |
209 | $data['Internet connectivity (Prestashop)'] = [
210 | false,
211 | true,
212 | gethostbyname('www.prestashop.com') !== 'www.prestashop.com',
213 | gethostbyname('www.prestashop.com') !== 'www.prestashop.com',
214 | ];
215 |
216 | return $data;
217 | }
218 |
219 | /**
220 | * Get php extensions data
221 | *
222 | * @return array
223 | */
224 | public function getPhpExtensions()
225 | {
226 | $data = [];
227 | $vars = [
228 | 'BCMath Arbitrary Precision Mathematics' => 'bcmath',
229 | 'Client URL Library (Curl)' => 'curl',
230 | 'Image Processing and GD' => 'gd',
231 | 'Image Processing (ImageMagick)' => 'imagick',
232 | 'Human Language and Character Encoding Support (Iconv)' => 'iconv',
233 | 'Internationalization Functions (Intl)' => 'intl',
234 | 'Memcache' => 'memcache',
235 | 'Memcached' => 'memcached',
236 | 'Multibyte String (Mbstring)' => 'mbstring',
237 | 'OpenSSL' => 'openssl',
238 | 'File Information (Fileinfo)' => 'fileinfo',
239 | 'JavaScript Object Notation (Json)' => 'json',
240 | 'PDO and MySQL Functions' => 'pdo_mysql',
241 | 'SimpleXML' => 'simplexml',
242 | ];
243 | foreach ($vars as $label => $var) {
244 | $value = extension_loaded($var);
245 | $data[$label] = [
246 | $this->requirements['extensions'][$var],
247 | $this->recommended['extensions'][$var],
248 | $value
249 | ];
250 | }
251 |
252 | $vars = [
253 | 'PHP-DOM and PHP-XML' => ['dom', 'DomDocument'],
254 | 'Zip' => ['zip', 'ZipArchive'],
255 | ];
256 | foreach ($vars as $label => $var) {
257 | $value = class_exists($var[1]);
258 | $data[$label] = [
259 | $this->requirements['extensions'][$var[0]],
260 | $this->recommended['extensions'][$var[0]],
261 | $value
262 | ];
263 | }
264 |
265 | return $data;
266 | }
267 |
268 | /**
269 | * Get php config data
270 | *
271 | * @return array
272 | */
273 | public function getPhpConfig()
274 | {
275 | $data = [];
276 | $vars = [
277 | 'allow_url_fopen',
278 | 'expose_php',
279 | 'file_uploads',
280 | 'register_argc_argv',
281 | 'short_open_tag',
282 | ];
283 | foreach ($vars as $var) {
284 | $value = (bool) ini_get($var);
285 | $data[$var] = [
286 | $this->requirements['config'][$var],
287 | $this->recommended['config'][$var],
288 | $value
289 | ];
290 | }
291 |
292 | $vars = [
293 | 'max_input_vars',
294 | 'memory_limit',
295 | 'post_max_size',
296 | 'upload_max_filesize',
297 | ];
298 | foreach ($vars as $var) {
299 | $value = ini_get($var);
300 | if ($this->toBytes($value) >= $this->toBytes($this->recommended['config'][$var])) {
301 | $result = self::TYPE_OK;
302 | } elseif ($this->toBytes($value) >= $this->toBytes($this->requirements['config'][$var])) {
303 | $result = self::TYPE_WARNING;
304 | } else {
305 | $result = self::TYPE_ERROR;
306 | }
307 |
308 | $data[$var] = [
309 | $this->requirements['config'][$var],
310 | $this->recommended['config'][$var],
311 | $value,
312 | $result,
313 | ];
314 | }
315 |
316 | $vars = [
317 | 'set_time_limit',
318 | ];
319 | foreach ($vars as $var) {
320 | $value = is_callable($var);
321 | $data[$var] = [
322 | $this->recommended['config'][$var],
323 | $this->requirements['config'][$var],
324 | $value
325 | ];
326 | }
327 |
328 | return $data;
329 | }
330 |
331 | /**
332 | * Check if directories are writable
333 | *
334 | * @return array
335 | */
336 | public function getDirectories()
337 | {
338 | $data = [];
339 | foreach ($this->requirements['directories'] as $directory) {
340 | $directoryPath = getcwd() . DIRECTORY_SEPARATOR . trim($directory, '\\/');
341 | $data[$directory] = file_exists($directoryPath) ? [is_writable($directoryPath)] : [null];
342 | }
343 |
344 | return $data;
345 | }
346 |
347 | public function getServerModules()
348 | {
349 | $data = [];
350 | if ($this->getWebServer() !== 'Apache' || !function_exists('apache_get_modules')) {
351 | return $data;
352 | }
353 |
354 | $modules = apache_get_modules();
355 | $vars = array_keys($this->requirements['apache_modules']);
356 | foreach ($vars as $var) {
357 | $value = in_array($var, $modules);
358 | $data[$var] = [
359 | $this->requirements['apache_modules'][$var],
360 | $this->recommended['apache_modules'][$var],
361 | $value,
362 | ];
363 | }
364 |
365 | return $data;
366 | }
367 |
368 | /**
369 | * Convert PHP variable (G/M/K) to bytes
370 | * Source: http://php.net/manual/fr/function.ini-get.php
371 | *
372 | * @param mixed $value
373 | *
374 | * @return integer
375 | */
376 | public function toBytes($value)
377 | {
378 | if (is_numeric($value)) {
379 | return $value;
380 | }
381 |
382 | $value = trim($value);
383 | $val = (int) $value;
384 | switch (strtolower($value[strlen($value)-1])) {
385 | case 'g':
386 | $val *= 1024;
387 | // continue
388 | case 'm':
389 | $val *= 1024;
390 | // continue
391 | case 'k':
392 | $val *= 1024;
393 | }
394 |
395 | return $val;
396 | }
397 |
398 | /**
399 | * Transform value to string
400 | *
401 | * @param mixed $value Value
402 | *
403 | * @return string
404 | */
405 | public function toString($value)
406 | {
407 | if ($value === true) {
408 | return 'Yes';
409 | } elseif ($value === false) {
410 | return 'No';
411 | } elseif ($value === null) {
412 | return 'N/A';
413 | }
414 |
415 | return strval($value);
416 | }
417 |
418 | /**
419 | * Get html class
420 | *
421 | * @param array $data
422 | * @return string
423 | */
424 | public function toHtmlClass(array $data)
425 | {
426 | if (count($data) === 1 && !is_bool($data[0])) {
427 | return self::TYPE_INFO_CLASS;
428 | }
429 |
430 |
431 | if (count($data) === 1 && is_bool($data[0])) {
432 | $result = $data[0];
433 | } elseif (array_key_exists(3, $data)) {
434 | $result = $data[3];
435 | } else {
436 | if ($data[2] >= $data[1]) {
437 | $result = self::TYPE_OK;
438 | } elseif ($data[2] >= $data[0]) {
439 | $result = self::TYPE_WARNING;
440 | } else {
441 | $result = self::TYPE_ERROR;
442 | }
443 | }
444 |
445 | if ($result === false) {
446 | return self::TYPE_ERROR_CLASS;
447 | }
448 |
449 | if ($result === null) {
450 | return self::TYPE_WARNING_CLASS;
451 | }
452 |
453 | return self::TYPE_SUCCESS_CLASS;
454 | }
455 |
456 | /**
457 | * Detect Web server
458 | *
459 | * @return string
460 | */
461 | protected function getWebServer()
462 | {
463 | if (stristr($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false) {
464 | return 'Apache';
465 | } elseif (stristr($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false) {
466 | return 'Lite Speed';
467 | } elseif (stristr($_SERVER['SERVER_SOFTWARE'], 'Nginx') !== false) {
468 | return 'Nginx';
469 | } elseif (stristr($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false) {
470 | return 'lighttpd';
471 | } elseif (stristr($_SERVER['SERVER_SOFTWARE'], 'IIS') !== false) {
472 | return 'Microsoft IIS';
473 | }
474 |
475 | return 'Not detected';
476 | }
477 |
478 | /**
479 | * Determines if a command exists on the current environment
480 | * Source: https://stackoverflow.com/questions/12424787/how-to-check-if-a-shell-command-exists-from-php
481 | *
482 | * @param string $command The command to check
483 | *
484 | * @return bool
485 | */
486 | protected function commandExists($command)
487 | {
488 | $which = (PHP_OS == 'WINNT') ? 'where' : 'which';
489 |
490 | $process = proc_open(
491 | $which . ' ' . $command,
492 | [
493 | ['pipe', 'r'], //STDIN
494 | ['pipe', 'w'], //STDOUT
495 | ['pipe', 'w'], //STDERR
496 | ],
497 | $pipes
498 | );
499 |
500 | if ($process !== false) {
501 | $stdout = stream_get_contents($pipes[1]);
502 | $stderr = stream_get_contents($pipes[2]);
503 | fclose($pipes[1]);
504 | fclose($pipes[2]);
505 | proc_close($process);
506 |
507 | return $stdout != '';
508 | }
509 |
510 | return false;
511 | }
512 | }
513 |
514 | // Init render
515 | $info = new PhpPsInfo();
516 | $info->checkAuth();
517 | ?>
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 | PHP PrestaShop Info
528 |
529 |
532 |
533 |
534 |
535 |
538 |
539 |
540 |
541 |
542 | General information & PHP/MySQL Version
543 |
544 |
545 |
546 |
547 | # |
548 | Required |
549 | Recommended |
550 | Current |
551 |
552 |
553 |
554 | getVersions() as $label => $data) : ?>
555 |
556 |
557 | |
558 | toString($data[0]) ?> |
559 |
560 |
561 |
562 | |
563 | toString($data[0]) ?> |
564 | toString($data[1]) ?> |
565 | toString($data[2]) ?> |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 | PHP Configuration
574 |
575 |
576 |
577 |
578 |
579 | # |
580 | Required |
581 | Recommended |
582 | Current |
583 |
584 |
585 |
586 | getPhpConfig() as $label => $data) : ?>
587 |
588 | |
589 | toString($data[0]) ?> |
590 | toString($data[1]) ?> |
591 | toString($data[2]) ?> |
592 |
593 |
594 |
595 |
596 |
597 |
598 | PHP Extensions
599 |
600 |
601 |
602 |
603 |
604 | # |
605 | Required |
606 | Recommended |
607 | Current |
608 |
609 |
610 |
611 | getPhpExtensions() as $label => $data) : ?>
612 |
613 | |
614 | toString($data[0]) ?> |
615 | toString($data[1]) ?> |
616 | toString($data[2]) ?> |
617 |
618 |
619 |
620 |
621 |
622 |
623 | Directories
624 |
625 |
626 |
627 |
628 |
629 | # |
630 | Is Writable |
631 |
632 |
633 |
634 | getDirectories() as $label => $data) : ?>
635 |
636 | |
637 |
638 | Directory not exists |
639 |
640 | toString($data[0]) ?> |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 | getServerModules()) > 0): ?>
649 | Apache Modules
650 |
651 |
652 |
653 |
654 |
655 | # |
656 | Required |
657 | Recommended |
658 | Current |
659 |
660 |
661 |
662 | getServerModules() as $label => $data) : ?>
663 |
664 | |
665 | toString($data[0]) ?> |
666 | toString($data[1]) ?> |
667 | toString($data[2]) ?> |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
681 |
682 |
683 |
--------------------------------------------------------------------------------