Completed
Push — develop ( 722f70...af048b )
by Jaap
15:12 queued 05:04
created

src/phpDocumentor/Application.php (3 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-2014 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 Cilex\Provider\ValidatorServiceProvider;
18
use Composer\Autoload\ClassLoader;
19
use Monolog\ErrorHandler;
20
use Monolog\Handler\NullHandler;
21
use Monolog\Handler\StreamHandler;
22
use Monolog\Logger;
23
use phpDocumentor\Command\Helper\ConfigurationHelper;
24
use phpDocumentor\Command\Helper\LoggerHelper;
25
use phpDocumentor\Console\Input\ArgvInput;
26
use Symfony\Component\Console\Application as ConsoleApplication;
27
use Symfony\Component\Console\Shell;
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
     * @param array       $values
45
     */
46
    public function __construct($autoloader = null, array $values = array())
47
    {
48
        $this->defineIniSettings();
49
        
50
        self::$VERSION = strpos('@package_version@', '@') === 0
51
            ? trim(file_get_contents(__DIR__ . '/../../VERSION'))
52
            : '@package_version@';
53
54
        parent::__construct('phpDocumentor', self::$VERSION, $values);
55
56
        $this['kernel.timer.start'] = time();
57
        $this['kernel.stopwatch'] = function () {
58
            return new Stopwatch();
59
        };
60
61
        $this['autoloader'] = $autoloader;
62
63
        $this->register(new JmsSerializerServiceProvider());
64
        $this->register(new Configuration\ServiceProvider());
65
66
        $this->addEventDispatcher();
67
        $this->addLogging();
68
69
        $this->register(new ValidatorServiceProvider());
70
        $this->register(new Translator\ServiceProvider());
71
        $this->register(new Descriptor\ServiceProvider());
72
        $this->register(new Parser\ServiceProvider());
73
        $this->register(new Partials\ServiceProvider());
74
        $this->register(new Transformer\ServiceProvider());
75
        $this->register(new Plugin\ServiceProvider());
76
77
        $this->addCommandsForProjectNamespace();
78
79
        if (\Phar::running()) {
80
            $this->addCommandsForPharNamespace();
81
        }
82
    }
83
84
    /**
85
     * Removes all logging handlers and replaces them with handlers that can write to the given logPath and level.
86
     *
87
     * @param Logger  $logger       The logger instance that needs to be configured.
88
     * @param integer $level        The minimum level that will be written to the normal logfile; matches one of the
89
     *                              constants in {@see \Monolog\Logger}.
90
     * @param string  $logPath      The full path where the normal log file needs to be written.
91
     *
92
     * @return void
93
     */
94
    public function configureLogger($logger, $level, $logPath = null)
95
    {
96
        /** @var Logger $monolog */
97
        $monolog = $logger;
98
99
        switch ($level) {
100
            case 'emergency':
101
            case 'emerg':
102
                $level = Logger::EMERGENCY;
103
                break;
104
            case 'alert':
105
                $level = Logger::ALERT;
106
                break;
107
            case 'critical':
108
            case 'crit':
109
                $level = Logger::CRITICAL;
110
                break;
111
            case 'error':
112
            case 'err':
113
                $level = Logger::ERROR;
114
                break;
115
            case 'warning':
116
            case 'warn':
117
                $level = Logger::WARNING;
118
                break;
119
            case 'notice':
120
                $level = Logger::NOTICE;
121
                break;
122
            case 'info':
123
                $level = Logger::INFO;
124
                break;
125
            case 'debug':
126
                $level = Logger::DEBUG;
127
                break;
128
        }
129
130
        $this['monolog.level']   = $level;
131
        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...
132
            $logPath = str_replace(
133
                array('{APP_ROOT}', '{DATE}'),
134
                array(realpath(__DIR__.'/../..'), $this['kernel.timer.start']),
135
                $logPath
136
            );
137
            $this['monolog.logfile'] = $logPath;
138
        }
139
140
        // remove all handlers from the stack
141
        try {
142
            while ($monolog->popHandler()) {
143
            }
144
        } catch (\LogicException $e) {
145
            // popHandler throws an exception when you try to pop the empty stack; to us this is not an
146
            // error but an indication that the handler stack is empty.
147
        }
148
149
        if ($level === 'quiet') {
150
            $monolog->pushHandler(new NullHandler());
151
152
            return;
153
        }
154
155
        // set our new handlers
156
        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...
157
            $monolog->pushHandler(new StreamHandler($logPath, $level));
158
        } else {
159
            $monolog->pushHandler(new StreamHandler('php://stdout', $level));
160
        }
161
    }
162
163
    /**
164
     * Run the application and if no command is provided, use project:run.
165
     *
166
     * @param bool $interactive Whether to run in interactive mode.
167
     *
168
     * @return void
169
     */
170
    public function run($interactive = false)
171
    {
172
        /** @var ConsoleApplication $app  */
173
        $app = $this['console'];
174
        $app->setAutoExit(false);
175
176
        if ($interactive) {
177
            $app = new Shell($app);
178
        }
179
180
        $output = new Console\Output\Output();
181
        $output->setLogger($this['monolog']);
182
183
        $app->run(new ArgvInput(), $output);
184
    }
185
186
    /**
187
     * Adjust php.ini settings.
188
     *
189
     * @return void
190
     */
191
    protected function defineIniSettings()
192
    {
193
        $this->setTimezone();
194
        ini_set('memory_limit', -1);
195
196
        // this code cannot be tested because we cannot control the system settings in unit tests
197
        // @codeCoverageIgnoreStart
198
        if (extension_loaded('Zend OPcache') && ini_get('opcache.enable') && ini_get('opcache.enable_cli')) {
199
            if (ini_get('opcache.save_comments')) {
200
                ini_set('opcache.load_comments', 1);
201
            } else {
202
                ini_set('opcache.enable', 0);
203
            }
204
        }
205
206
        if (extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.save_comments') == 0) {
207
            throw new \RuntimeException('Please enable zend_optimizerplus.save_comments in php.ini.');
208
        }
209
        // @codeCoverageIgnoreEnd
210
    }
211
212
    /**
213
     * If the timezone is not set anywhere, set it to UTC.
214
     *
215
     * This is done to prevent any warnings being outputted in relation to using
216
     * date/time functions. What is checked is php.ini, and if the PHP version
217
     * is prior to 5.4, the TZ environment variable.
218
     *
219
     * @link http://php.net/manual/en/function.date-default-timezone-get.php for more information how PHP determines the
220
     *     default timezone.
221
     *
222
     * @codeCoverageIgnore this method is very hard, if not impossible, to unit test and not critical.
223
     *
224
     * @return void
225
     */
226
    protected function setTimezone()
227
    {
228
        if (false == ini_get('date.timezone')
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing ini_get('date.timezone') of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
229
            || (version_compare(phpversion(), '5.4.0', '<') && false === getenv('TZ'))
230
        ) {
231
            date_default_timezone_set('UTC');
232
        }
233
    }
234
235
    /**
236
     * Adds a logging provider to the container of phpDocumentor.
237
     *
238
     * @return void
239
     */
240
    protected function addLogging()
241
    {
242
        $this->register(
243
            new MonologServiceProvider(),
244
            array(
245
                'monolog.name'      => 'phpDocumentor',
246
                'monolog.logfile'   => sys_get_temp_dir() . '/phpdoc.log',
247
                'monolog.debugfile' => sys_get_temp_dir() . '/phpdoc.debug.log',
248
                'monolog.level'     => Logger::INFO,
249
            )
250
        );
251
252
        $app = $this;
253
        /** @var Configuration $configuration */
254
        $configuration = $this['config'];
255
        $this['monolog.configure'] = $this->protect(
256
            function ($log) use ($app, $configuration) {
257
                $paths    = $configuration->getLogging()->getPaths();
258
                $logLevel = $configuration->getLogging()->getLevel();
259
260
                $app->configureLogger($log, $logLevel, $paths['default'], $paths['errors']);
261
            }
262
        );
263
264
        $this->extend(
265
            'console',
266
            function (ConsoleApplication $console) use ($configuration) {
267
                $console->getHelperSet()->set(new LoggerHelper());
268
                $console->getHelperSet()->set(new ConfigurationHelper($configuration));
269
270
                return $console;
271
            }
272
        );
273
274
        ErrorHandler::register($this['monolog']);
275
    }
276
277
    /**
278
     * Adds the event dispatcher to phpDocumentor's container.
279
     *
280
     * @return void
281
     */
282
    protected function addEventDispatcher()
283
    {
284
        $this['event_dispatcher'] = $this->share(
285
            function () {
286
                return Event\Dispatcher::getInstance();
287
            }
288
        );
289
    }
290
291
    /**
292
     * Adds the command to phpDocumentor that belong to the Project namespace.
293
     *
294
     * @return void
295
     */
296
    protected function addCommandsForProjectNamespace()
297
    {
298
        $this->command(new Command\Project\RunCommand());
299
    }
300
301
    /**
302
     * Adds the command to phpDocumentor that belong to the Phar namespace.
303
     *
304
     * @return void
305
     */
306
    protected function addCommandsForPharNamespace()
307
    {
308
        $this->command(new Command\Phar\UpdateCommand());
309
    }
310
}
311