1<?php
2/*
3 * This file is part of the Environment package.
4 *
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11namespace SebastianBergmann\Environment;
12
13/**
14 * Utility class for HHVM/PHP environment handling.
15 */
16class Runtime
17{
18    /**
19     * @var string
20     */
21    private static $binary;
22
23    /**
24     * Returns true when Xdebug is supported or
25     * the runtime used is PHPDBG (PHP >= 7.0).
26     *
27     * @return bool
28     */
29    public function canCollectCodeCoverage()
30    {
31        return $this->hasXdebug() || $this->hasPHPDBGCodeCoverage();
32    }
33
34    /**
35     * Returns the path to the binary of the current runtime.
36     * Appends ' --php' to the path when the runtime is HHVM.
37     *
38     * @return string
39     */
40    public function getBinary()
41    {
42        // HHVM
43        if (self::$binary === null && $this->isHHVM()) {
44            if ((self::$binary = getenv('PHP_BINARY')) === false) {
45                self::$binary = PHP_BINARY;
46            }
47
48            self::$binary = escapeshellarg(self::$binary) . ' --php';
49        }
50
51        // PHP >= 5.4.0
52        if (self::$binary === null && defined('PHP_BINARY')) {
53            if (PHP_BINARY !== '') {
54                self::$binary = escapeshellarg(PHP_BINARY);
55            }
56        }
57
58        // PHP < 5.4.0
59        if (self::$binary === null) {
60            if (PHP_SAPI == 'cli' && isset($_SERVER['_'])) {
61                if (strpos($_SERVER['_'], 'phpunit') !== false) {
62                    $file = file($_SERVER['_']);
63
64                    if (strpos($file[0], ' ') !== false) {
65                        $tmp          = explode(' ', $file[0]);
66                        self::$binary = escapeshellarg(trim($tmp[1]));
67                    } else {
68                        self::$binary = escapeshellarg(ltrim(trim($file[0]), '#!'));
69                    }
70                } elseif (strpos(basename($_SERVER['_']), 'php') !== false) {
71                    self::$binary = escapeshellarg($_SERVER['_']);
72                }
73            }
74        }
75
76        if (self::$binary === null) {
77            $possibleBinaryLocations = [
78                PHP_BINDIR . '/php',
79                PHP_BINDIR . '/php-cli.exe',
80                PHP_BINDIR . '/php.exe'
81            ];
82
83            foreach ($possibleBinaryLocations as $binary) {
84                if (is_readable($binary)) {
85                    self::$binary = escapeshellarg($binary);
86                    break;
87                }
88            }
89        }
90
91        if (self::$binary === null) {
92            self::$binary = 'php';
93        }
94
95        return self::$binary;
96    }
97
98    /**
99     * @return string
100     */
101    public function getNameWithVersion()
102    {
103        return $this->getName() . ' ' . $this->getVersion();
104    }
105
106    /**
107     * @return string
108     */
109    public function getName()
110    {
111        if ($this->isHHVM()) {
112            return 'HHVM';
113        } elseif ($this->isPHPDBG()) {
114            return 'PHPDBG';
115        } else {
116            return 'PHP';
117        }
118    }
119
120    /**
121     * @return string
122     */
123    public function getVendorUrl()
124    {
125        if ($this->isHHVM()) {
126            return 'http://hhvm.com/';
127        } else {
128            return 'https://secure.php.net/';
129        }
130    }
131
132    /**
133     * @return string
134     */
135    public function getVersion()
136    {
137        if ($this->isHHVM()) {
138            return HHVM_VERSION;
139        } else {
140            return PHP_VERSION;
141        }
142    }
143
144    /**
145     * Returns true when the runtime used is PHP and Xdebug is loaded.
146     *
147     * @return bool
148     */
149    public function hasXdebug()
150    {
151        return ($this->isPHP() || $this->isHHVM()) && extension_loaded('xdebug');
152    }
153
154    /**
155     * Returns true when the runtime used is HHVM.
156     *
157     * @return bool
158     */
159    public function isHHVM()
160    {
161        return defined('HHVM_VERSION');
162    }
163
164    /**
165     * Returns true when the runtime used is PHP without the PHPDBG SAPI.
166     *
167     * @return bool
168     */
169    public function isPHP()
170    {
171        return !$this->isHHVM() && !$this->isPHPDBG();
172    }
173
174    /**
175     * Returns true when the runtime used is PHP with the PHPDBG SAPI.
176     *
177     * @return bool
178     */
179    public function isPHPDBG()
180    {
181        return PHP_SAPI === 'phpdbg' && !$this->isHHVM();
182    }
183
184    /**
185     * Returns true when the runtime used is PHP with the PHPDBG SAPI
186     * and the phpdbg_*_oplog() functions are available (PHP >= 7.0).
187     *
188     * @return bool
189     */
190    public function hasPHPDBGCodeCoverage()
191    {
192        return $this->isPHPDBG() && function_exists('phpdbg_start_oplog');
193    }
194}
195