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

src/phpDocumentor/Application.php (4 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();
0 ignored issues
show
The call to the method phpDocumentor\Application::addEventDispatcher() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
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()) {
0 ignored issues
show
This while loop is empty and can be removed.

This check looks for while loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
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