Completed
Push — develop ( 4b49c4...89d32a )
by Jaap
09:06 queued 05:30
created

src/phpDocumentor/Application.php (1 issue)

Severity

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\Input\InputInterface;
28
use Symfony\Component\Console\Output\OutputInterface;
29
use Symfony\Component\Console\Shell;
30
use Symfony\Component\Stopwatch\Stopwatch;
31
32
/**
33
 * Application class for phpDocumentor.
34
 *
35
 * Can be used as bootstrap when the run method is not invoked.
36
 */
37
class Application extends Cilex
38
{
39
    /** @var string $VERSION represents the version of phpDocumentor as stored in /VERSION */
40
    public static $VERSION;
41
42
    /**
43
     * Initializes all components used by phpDocumentor.
44
     *
45
     * @param ClassLoader $autoloader
46
     * @param array $values
47
     */
48 14
    public function __construct($autoloader = null, array $values = array())
49
    {
50 14
        $this->defineIniSettings();
51
52 14
        self::$VERSION = strpos('@package_version@', '@') === 0
53 14
            ? trim(file_get_contents(__DIR__ . '/../../VERSION'))
54 14
            : '@package_version@';
55
56 14
        parent::__construct('phpDocumentor', self::$VERSION, $values);
57
58 14
        $this['kernel.timer.start'] = time();
59 14
        $this['kernel.stopwatch'] = function () {
60 14
            return new Stopwatch();
61
        };
62
63 14
        $this['autoloader'] = $autoloader;
64
65 14
        $this->register(new JmsSerializerServiceProvider());
66 14
        $this->register(new Configuration\ServiceProvider());
67
68 14
        $this->addEventDispatcher();
69 14
        $this->addLogging();
70
71 14
        $this->register(new Translator\ServiceProvider());
72 14
        $this->register(new Descriptor\ServiceProvider());
73 14
        $this->register(new Partials\ServiceProvider());
74 14
        $this->register(new Parser\ServiceProvider());
75 14
        $this->register(new Transformer\ServiceProvider());
76 14
        $this->register(new Plugin\ServiceProvider());
77
78 14
        $this->addCommandsForProjectNamespace();
79
80 14
        if (\Phar::running()) {
81
            $this->addCommandsForPharNamespace();
82
        }
83 14
    }
84
85
    /**
86
     * Removes all logging handlers and replaces them with handlers that can write to the given logPath and level.
87
     *
88
     * @param Logger  $logger       The logger instance that needs to be configured.
89
     * @param integer $level        The minimum level that will be written to the normal logfile; matches one of the
90
     *                              constants in {@see \Monolog\Logger}.
91
     * @param string  $logPath      The full path where the normal log file needs to be written.
92
     *
93
     * @return void
94
     */
95 11
    public function configureLogger($logger, $level, $logPath = null)
96
    {
97
        /** @var Logger $monolog */
98 11
        $monolog = $logger;
99
100
        switch ($level) {
101 11
            case 'emergency':
102 11
            case 'emerg':
103 2
                $level = Logger::EMERGENCY;
104 2
                break;
105 11
            case 'alert':
106 1
                $level = Logger::ALERT;
107 1
                break;
108 11
            case 'critical':
109 11
            case 'crit':
110 1
                $level = Logger::CRITICAL;
111 1
                break;
112 11
            case 'error':
113 6
            case 'err':
114 11
                $level = Logger::ERROR;
115 11
                break;
116 5
            case 'warning':
117 4
            case 'warn':
118 2
                $level = Logger::WARNING;
119 2
                break;
120 3
            case 'notice':
121 1
                $level = Logger::NOTICE;
122 1
                break;
123 2
            case 'info':
124 1
                $level = Logger::INFO;
125 1
                break;
126 1
            case 'debug':
127 1
                $level = Logger::DEBUG;
128 1
                break;
129
        }
130
131 11
        $this['monolog.level'] = $level;
132 11
        if ($logPath) {
133
            $logPath = str_replace(
134
                array('{APP_ROOT}', '{DATE}'),
135
                array(realpath(__DIR__ . '/../..'), $this['kernel.timer.start']),
136
                $logPath
137
            );
138
            $this['monolog.logfile'] = $logPath;
139
        }
140
141
        // remove all handlers from the stack
142
        try {
143 11
            while ($monolog->popHandler()) {
144
            }
145 11
        } catch (\LogicException $e) {
146
            // popHandler throws an exception when you try to pop the empty stack; to us this is not an
147
            // error but an indication that the handler stack is empty.
148
        }
149
150 11
        if ($level === 'quiet') {
151
            $monolog->pushHandler(new NullHandler());
152
153
            return;
154
        }
155
156
        // set our new handlers
157 11
        if ($logPath) {
158
            $monolog->pushHandler(new StreamHandler($logPath, $level));
159
        } else {
160 11
            $monolog->pushHandler(new StreamHandler('php://stdout', $level));
161
        }
162 11
    }
163
164
    /**
165
     * @param null|InputInterface $input
166
     * @param null|OutputInterface $output
167
     * @return mixed
168
     */
169
    public function run(InputInterface $input = null, OutputInterface $output = null)
170
    {
171
        /** @var ConsoleApplication $app */
172
        $app = $this['console'];
173
        $app->setAutoExit(false);
174
175
        if ($output === null) {
176
            $output = new Console\Output\Output();
177
            $output->setLogger($this['monolog']);
178
        }
179
180
        return parent::run(new ArgvInput(), $output);
181
    }
182
183
184
    /**
185
     * Adjust php.ini settings.
186
     *
187
     * @return void
188
     */
189 1
    protected function defineIniSettings()
190
    {
191 1
        $this->setTimezone();
192 1
        ini_set('memory_limit', -1);
193
194
        // this code cannot be tested because we cannot control the system settings in unit tests
195
        // @codeCoverageIgnoreStart
196
        if (extension_loaded('Zend OPcache') && ini_get('opcache.enable') && ini_get('opcache.enable_cli')) {
197
            if (ini_get('opcache.save_comments')) {
198
                ini_set('opcache.load_comments', 1);
199
            } else {
200
                ini_set('opcache.enable', 0);
201
            }
202
        }
203
204
        if (extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.save_comments') == 0) {
205
            throw new \RuntimeException('Please enable zend_optimizerplus.save_comments in php.ini.');
206
        }
207
        // @codeCoverageIgnoreEnd
208 1
    }
209
210
    /**
211
     * If the timezone is not set anywhere, set it to UTC.
212
     *
213
     * This is done to prevent any warnings being outputted in relation to using
214
     * date/time functions. What is checked is php.ini, and if the PHP version
215
     * is prior to 5.4, the TZ environment variable.
216
     *
217
     * @link http://php.net/manual/en/function.date-default-timezone-get.php for more information how PHP determines the
218
     *     default timezone.
219
     *
220
     * @codeCoverageIgnore this method is very hard, if not impossible, to unit test and not critical.
221
     *
222
     * @return void
223
     */
224
    protected function setTimezone()
225
    {
226
        if (false == ini_get('date.timezone')
227
            || (version_compare(phpversion(), '5.4.0', '<') && false === getenv('TZ'))
228
        ) {
229
            date_default_timezone_set('UTC');
230
        }
231
    }
232
233
    /**
234
     * Adds a logging provider to the container of phpDocumentor.
235
     *
236
     * @return void
237
     */
238
    protected function addLogging()
239
    {
240
        $this->register(
241
            new MonologServiceProvider(),
242
            array(
243
                'monolog.name'      => 'phpDocumentor',
244
                'monolog.logfile'   => sys_get_temp_dir() . '/phpdoc.log',
245
                'monolog.debugfile' => sys_get_temp_dir() . '/phpdoc.debug.log',
246
                'monolog.level'     => Logger::INFO,
247
            )
248
        );
249
250
        $app = $this;
251
        /** @var Configuration $configuration */
252
        $configuration = $this['config'];
253
        $this['monolog.configure'] = $this->protect(
254
            function ($log) use ($app, $configuration) {
255
                $paths    = $configuration->getLogging()->getPaths();
256
                $logLevel = $configuration->getLogging()->getLevel();
257
258
                $app->configureLogger($log, $logLevel, $paths['default'], $paths['errors']);
0 ignored issues
show
The call to Application::configureLogger() has too many arguments starting with $paths['errors'].

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
259
            }
260
        );
261
262
        $this->extend(
263
            'console',
264
            function (ConsoleApplication $console) use ($configuration) {
265
                $console->getHelperSet()->set(new LoggerHelper());
266
                $console->getHelperSet()->set(new ConfigurationHelper($configuration));
267
268
                return $console;
269
            }
270
        );
271
272
        ErrorHandler::register($this['monolog']);
273
    }
274
275
    /**
276
     * Adds the event dispatcher to phpDocumentor's container.
277
     *
278
     * @return void
279
     */
280
    protected function addEventDispatcher()
281
    {
282 1
        $this['event_dispatcher'] = function () {
283 1
            return Event\Dispatcher::getInstance();
284
        };
285 1
    }
286
287
    /**
288
     * Adds the command to phpDocumentor that belong to the Project namespace.
289
     *
290
     * @return void
291
     */
292 1
    protected function addCommandsForProjectNamespace()
293
    {
294 1
        $this->command(new Command\Project\RunCommand());
295 1
    }
296
297
    /**
298
     * Adds the command to phpDocumentor that belong to the Phar namespace.
299
     *
300
     * @return void
301
     */
302
    protected function addCommandsForPharNamespace()
303
    {
304
        $this->command(new Command\Phar\UpdateCommand());
305
    }
306
}
307