Completed
Push — develop ( 8fda15...dbbcf5 )
by Jaap
14s
created

src/phpDocumentor/Application.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * phpDocumentor
4
 *
5
 * PHP Version 5.3
6
 *
7
 * @copyright 2010-2018 Mike van Riel / Naenius (http://www.naenius.com)
8
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
9
 * @link      http://phpdoc.org
10
 */
11
12
namespace phpDocumentor;
13
14
use Cilex\Application as Cilex;
15
use Cilex\Provider\JmsSerializerServiceProvider;
16
use Cilex\Provider\MonologServiceProvider;
17
use Composer\Autoload\ClassLoader;
18
use Monolog\ErrorHandler;
19
use Monolog\Handler\NullHandler;
20
use Monolog\Handler\StreamHandler;
21
use Monolog\Logger;
22
use phpDocumentor\Command\Helper\ConfigurationHelper;
23
use phpDocumentor\Command\Helper\LoggerHelper;
24
use phpDocumentor\Console\Input\ArgvInput;
25
use Symfony\Component\Console\Application as ConsoleApplication;
26
use Symfony\Component\Console\Input\InputInterface;
27
use Symfony\Component\Console\Output\OutputInterface;
28
use Symfony\Component\Stopwatch\Stopwatch;
29
30
/**
31
 * Application class for phpDocumentor.
32
 *
33
 * Can be used as bootstrap when the run method is not invoked.
34
 */
35
class Application extends Cilex
36
{
37
    /** @var string $VERSION represents the version of phpDocumentor as stored in /VERSION */
38
    public static $VERSION;
39
40
    /**
41
     * Initializes all components used by phpDocumentor.
42
     *
43
     * @param ClassLoader $autoloader
44
     */
45 14
    public function __construct($autoloader = null, array $values = [])
46
    {
47 14
        $this->defineIniSettings();
48
49 14
        self::$VERSION = strpos('@package_version@', '@') === 0
50 14
            ? trim(file_get_contents(__DIR__ . '/../../VERSION'))
51 14
            : '@package_version@';
52
53 14
        parent::__construct('phpDocumentor', self::$VERSION, $values);
54
55 14
        $this['kernel.timer.start'] = time();
56 14
        $this['kernel.stopwatch'] = function () {
57 14
            return new Stopwatch();
58
        };
59
60 14
        $this['autoloader'] = $autoloader;
61
62 14
        $this->register(new JmsSerializerServiceProvider());
63 14
        $this->register(new Configuration\ServiceProvider());
64
65 14
        $this->addEventDispatcher();
66 14
        $this->addLogging();
67
68 14
        $this->register(new Translator\ServiceProvider());
69 14
        $this->register(new Descriptor\ServiceProvider());
70 14
        $this->register(new Partials\ServiceProvider());
71 14
        $this->register(new Parser\ServiceProvider());
72 14
        $this->register(new Transformer\ServiceProvider());
73 14
        $this->register(new Plugin\ServiceProvider());
74
75 14
        $this->addCommandsForProjectNamespace();
76
77 14
        if (\Phar::running()) {
78
            $this->addCommandsForPharNamespace();
79
        }
80 14
    }
81
82
    /**
83
     * Removes all logging handlers and replaces them with handlers that can write to the given logPath and level.
84
     *
85
     * @param Logger  $logger       The logger instance that needs to be configured.
86
     * @param integer $level        The minimum level that will be written to the normal logfile; matches one of the
87
     *                              constants in {@see \Monolog\Logger}.
88
     * @param string  $logPath      The full path where the normal log file needs to be written.
89
     */
90 11
    public function configureLogger($logger, $level, $logPath = null)
91
    {
92
        /** @var Logger $monolog */
93 11
        $monolog = $logger;
94
95
        switch ($level) {
96 11
            case 'emergency':
97 11
            case 'emerg':
98 2
                $level = Logger::EMERGENCY;
99 2
                break;
100 11
            case 'alert':
101 1
                $level = Logger::ALERT;
102 1
                break;
103 11
            case 'critical':
104 11
            case 'crit':
105 1
                $level = Logger::CRITICAL;
106 1
                break;
107 11
            case 'error':
108 6
            case 'err':
109 11
                $level = Logger::ERROR;
110 11
                break;
111 5
            case 'warning':
112 4
            case 'warn':
113 2
                $level = Logger::WARNING;
114 2
                break;
115 3
            case 'notice':
116 1
                $level = Logger::NOTICE;
117 1
                break;
118 2
            case 'info':
119 1
                $level = Logger::INFO;
120 1
                break;
121 1
            case 'debug':
122 1
                $level = Logger::DEBUG;
123 1
                break;
124
        }
125
126 11
        $this['monolog.level'] = $level;
127 11
        if ($logPath) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $logPath of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
128
            $logPath = str_replace(
129
                ['{APP_ROOT}', '{DATE}'],
130
                [realpath(__DIR__ . '/../..'), $this['kernel.timer.start']],
131
                $logPath
132
            );
133
            $this['monolog.logfile'] = $logPath;
134
        }
135
136
        // remove all handlers from the stack
137
        try {
138 11
            while ($monolog->popHandler()) {
139
            }
140 11
        } catch (\LogicException $e) {
141
            // popHandler throws an exception when you try to pop the empty stack; to us this is not an
142
            // error but an indication that the handler stack is empty.
143
        }
144
145 11
        if ($level === 'quiet') {
146
            $monolog->pushHandler(new NullHandler());
147
148
            return;
149
        }
150
151
        // set our new handlers
152 11
        if ($logPath) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $logPath of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
153
            $monolog->pushHandler(new StreamHandler($logPath, $level));
154
        } else {
155 11
            $monolog->pushHandler(new StreamHandler('php://stdout', $level));
156
        }
157 11
    }
158
159
    /**
160
     * @return mixed
161
     */
162
    public function run(InputInterface $input = null, OutputInterface $output = null)
163
    {
164
        /** @var ConsoleApplication $app */
165
        $app = $this['console'];
166
        $app->setAutoExit(false);
167
168
        if ($output === null) {
169
            $output = new Console\Output\Output();
170
            $output->setLogger($this['monolog']);
171
        }
172
173
        return parent::run(new ArgvInput(), $output);
174
    }
175
176
    /**
177
     * Adjust php.ini settings.
178
     */
179 1
    protected function defineIniSettings()
180
    {
181 1
        $this->setTimezone();
182 1
        ini_set('memory_limit', -1);
183
184
        // this code cannot be tested because we cannot control the system settings in unit tests
185
        // @codeCoverageIgnoreStart
186
        if (extension_loaded('Zend OPcache') && ini_get('opcache.enable') && ini_get('opcache.enable_cli')) {
187
            if (ini_get('opcache.save_comments')) {
188
                ini_set('opcache.load_comments', 1);
189
            } else {
190
                ini_set('opcache.enable', 0);
191
            }
192
        }
193
194
        if (extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.save_comments') === 0) {
195
            throw new \RuntimeException('Please enable zend_optimizerplus.save_comments in php.ini.');
196
        }
197
198
        // @codeCoverageIgnoreEnd
199 1
    }
200
201
    /**
202
     * If the timezone is not set anywhere, set it to UTC.
203
     *
204
     * This is done to prevent any warnings being outputted in relation to using
205
     * date/time functions. What is checked is php.ini, and if the PHP version
206
     * is prior to 5.4, the TZ environment variable.
207
     *
208
     * @link http://php.net/manual/en/function.date-default-timezone-get.php for more information how PHP determines the
209
     *     default timezone.
210
     *
211
     * @codeCoverageIgnore this method is very hard, if not impossible, to unit test and not critical.
212
     */
213
    protected function setTimezone()
214
    {
215
        if (false === ini_get('date.timezone')
216
            || (version_compare(phpversion(), '5.4.0', '<') && false === getenv('TZ'))
217
        ) {
218
            date_default_timezone_set('UTC');
219
        }
220
    }
221
222
    /**
223
     * Adds a logging provider to the container of phpDocumentor.
224
     */
225
    protected function addLogging()
226
    {
227
        $this->register(
228
            new MonologServiceProvider(),
229
            [
230
                'monolog.name' => 'phpDocumentor',
231
                'monolog.logfile' => sys_get_temp_dir() . '/phpdoc.log',
232
                'monolog.debugfile' => sys_get_temp_dir() . '/phpdoc.debug.log',
233
                'monolog.level' => Logger::INFO,
234
            ]
235
        );
236
237
        $app = $this;
238
        /** @var Configuration $configuration */
239
        $configuration = $this['config'];
240
        $this['monolog.configure'] = $this->protect(
241
            function ($log) use ($app, $configuration) {
242
                $paths = $configuration->getLogging()->getPaths();
243
                $logLevel = $configuration->getLogging()->getLevel();
244
245
                $app->configureLogger($log, $logLevel, $paths['default']);
246
            }
247
        );
248
249
        $this->extend(
250
            'console',
251
            function (ConsoleApplication $console) use ($configuration) {
252
                $console->getHelperSet()->set(new LoggerHelper());
253
                $console->getHelperSet()->set(new ConfigurationHelper($configuration));
254
255
                return $console;
256
            }
257
        );
258
259
        ErrorHandler::register($this['monolog']);
260
    }
261
262
    /**
263
     * Adds the event dispatcher to phpDocumentor's container.
264
     */
265
    protected function addEventDispatcher()
266
    {
267 1
        $this['event_dispatcher'] = function () {
268 1
            return Event\Dispatcher::getInstance();
269
        };
270 1
    }
271
272
    /**
273
     * Adds the command to phpDocumentor that belong to the Project namespace.
274
     */
275 1
    protected function addCommandsForProjectNamespace()
276
    {
277 1
        $this->command(new Command\Project\RunCommand());
278 1
    }
279
280
    /**
281
     * Adds the command to phpDocumentor that belong to the Phar namespace.
282
     */
283
    protected function addCommandsForPharNamespace()
284
    {
285
        $this->command(new Command\Phar\UpdateCommand());
286
    }
287
}
288