GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Output   B
last analyzed

Complexity

Total Complexity 52

Size/Duplication

Total Lines 580
Duplicated Lines 10.86 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 3
Bugs 1 Features 0
Metric Value
wmc 52
c 3
b 1
f 0
lcom 1
cbo 4
dl 63
loc 580
rs 7.9487

28 Methods

Rating   Name   Duplication   Size   Complexity  
A usePluginAsConsole() 0 4 1
A getPlugins() 0 4 1
A __construct() 0 8 1
A usePluginInSlot() 0 8 1
A getActiveConsolePlugin() 0 5 1
A getActivePluginInSlot() 0 13 2
A resetSilentMode() 0 7 2
A setSilentMode() 0 7 2
A setIsVerbose() 0 7 2
A disableColourSupport() 0 7 2
A enforceColourSupport() 0 7 2
A enableColourSupport() 0 7 2
A startStoryplayer() 0 14 2
A endStoryplayer() 0 11 2
A startPhaseGroup() 0 12 2
A endPhaseGroup() 0 11 2
A startPhase() 0 10 2
A endPhase() 0 16 2
A logPhaseActivity() 23 23 3
A logPhaseSubprocessOutput() 0 20 2
A logPhaseError() 20 20 2
A logPhaseSkipped() 20 20 2
A logPhaseCodeLine() 0 11 2
A logCliError() 0 11 2
A logCliErrorWithException() 0 12 2
A logCliWarning() 0 11 2
A logCliInfo() 0 11 2
A logVardump() 0 12 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Output often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Output, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Copyright (c) 2011-present Mediasift Ltd
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 *
11
 *   * Redistributions of source code must retain the above copyright
12
 *     notice, this list of conditions and the following disclaimer.
13
 *
14
 *   * Redistributions in binary form must reproduce the above copyright
15
 *     notice, this list of conditions and the following disclaimer in
16
 *     the documentation and/or other materials provided with the
17
 *     distribution.
18
 *
19
 *   * Neither the names of the copyright holders nor the names of his
20
 *     contributors may be used to endorse or promote products derived
21
 *     from this software without specific prior written permission.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
 * POSSIBILITY OF SUCH DAMAGE.
35
 *
36
 * @category  Libraries
37
 * @package   Storyplayer/OutputLib
38
 * @author    Stuart Herbert <[email protected]>
39
 * @copyright 2011-present Mediasift Ltd www.datasift.com
40
 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
41
 * @link      http://datasift.github.io/storyplayer
42
 */
43
44
namespace DataSift\Storyplayer;
45
46
use Exception;
47
use DataSift\Storyplayer\Phases\Phase;
48
use DataSift\Storyplayer\PlayerLib\Phase_Result;
49
use DataSift\Storyplayer\PlayerLib\PhaseGroup_Result;
50
use DataSift\Storyplayer\PlayerLib\Story_Result;
51
use DataSift\Storyplayer\OutputLib\OutputPlugin;
52
use DataSift\Storyplayer\Console\DefaultConsole;
53
use DataSift\Storyplayer\Console\Console;
54
55
use Phix_Project\ContractLib2\Contract;
56
57
/**
58
 * all output goes through here
59
 *
60
 * @category  Libraries
61
 * @package   Storyplayer/OutputLib
62
 * @author    Stuart Herbert <[email protected]>
63
 * @copyright 2011-present Mediasift Ltd www.datasift.com
64
 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
65
 * @link      http://datasift.github.io/storyplayer
66
 */
67
class Output extends OutputPlugin
68
{
69
    /**
70
     * a list of the plugins that are currently active
71
     *
72
     * @var array
73
     */
74
    protected $plugins = [];
75
76
    /**
77
     * a list of the log messages that we have been asked to output
78
     *
79
     * this is used for producing detailed error reports when something
80
     * has gone badly wrong
81
     *
82
     * @var array
83
     */
84
    protected $activityLog = [];
85
86
    /**
87
     * constructor
88
     *
89
     * ensures we have a default console that is connected to stdout
90
     */
91
    public function __construct()
92
    {
93
        // we need a default output for the console
94
        $console = new DefaultConsole();
95
        $console->addOutputToStdout();
96
97
        $this->usePluginAsConsole($console);
98
    }
99
100
    /**
101
     * make a plugin the one that we use when writing to the user's
102
     * console
103
     *
104
     * @param  Console $plugin
105
     *         the plugin that we want
106
     *
107
     * @return void
108
     */
109
    public function usePluginAsConsole(Console $plugin)
110
    {
111
        $this->plugins['console'] = $plugin;
112
    }
113
114
    /**
115
     * set the plugin for a named output slot
116
     *
117
     * @param OutputPlugin $plugin
118
     *        the plugin to use in the slot
119
     * @param string       $slotName
120
     *        the name of the slot to use for this plugin
121
     */
122
    public function usePluginInSlot(OutputPlugin $plugin, $slotName)
123
    {
124
        // enforce our inputs
125
        Contract::RequiresValue($slotName, is_string($slotName));
126
127
        // put the plugin in the required slot
128
        $this->plugins[$slotName] = $plugin;
129
    }
130
131
    /**
132
     * get the array of all plugins
133
     *
134
     * @return array
135
     */
136
    public function getPlugins()
137
    {
138
        return $this->plugins;
139
    }
140
141
    /**
142
     * return the active plugin in the 'console' slot
143
     *
144
     * @return Console|null
145
     */
146
    public function getActiveConsolePlugin()
147
    {
148
        // we ALWAYS have a console plugin :)
149
        return $this->plugins['console'];
150
    }
151
152
    /**
153
     * return the active plugin in the named slot
154
     *
155
     * @param  string $slotName
156
     * @return OutputPlugin|null
157
     */
158
    public function getActivePluginInSlot($slotName)
159
    {
160
        // enforce our inputs
161
        Contract::RequiresValue($slotName, is_string($slotName));
162
163
        // do we have a plugin in this slot?
164
        if (isset($this->plugins[$slotName])) {
165
            return $this->plugins[$slotName];
166
        }
167
168
        // no, we do not
169
        return null;
170
    }
171
172
    /**
173
     * disable 'silent' mode
174
     *
175
     * NOTE: it is up to each plugin in turn whether or not to support
176
     * 'silent' mode at all
177
     *
178
     * @return void
179
     */
180
    public function resetSilentMode()
181
    {
182
        foreach ($this->plugins as $plugin)
183
        {
184
            $plugin->resetSilentMode();
185
        }
186
    }
187
188
    /**
189
     * switches 'silent' mode on
190
     *
191
     * in 'silent' mode, we do not write log activity to the output writer
192
     * at all.  HOWEVER, the plugin may still add the log activity to any
193
     * internal cache it has (can be useful for error reports etc)
194
     *
195
     * @return void
196
     */
197
    public function setSilentMode()
198
    {
199
        foreach ($this->plugins as $plugin)
200
        {
201
            $plugin->setSilentMode();
202
        }
203
    }
204
205
    /**
206
     * switches 'verbose' mode on or off
207
     *
208
     * in 'non-verbose' mode, each output plugin is free to supress some of
209
     * the output, for the sake of asthetics
210
     *
211
     * @param boolean $isVerbose
212
     *        do we want verbose mode or not?
213
     */
214
    public function setIsVerbose($isVerbose)
215
    {
216
        foreach ($this->plugins as $plugin)
217
        {
218
            $plugin->setIsVerbose($isVerbose);
219
        }
220
    }
221
222
    /**
223
     * disables any colour output
224
     *
225
     * @return void
226
     */
227
    public function disableColourSupport()
228
    {
229
        foreach ($this->plugins as $plugin)
230
        {
231
            $plugin->disableColourSupport();
232
        }
233
    }
234
235
    /**
236
     * forces switching on colour support
237
     *
238
     * @return void
239
     */
240
    public function enforceColourSupport()
241
    {
242
        foreach ($this->plugins as $plugin)
243
        {
244
            $plugin->enforceColourSupport();
245
        }
246
    }
247
248
    /**
249
     * asks each active plugin to switch on colour support if possible
250
     *
251
     * a plugin may still choose to not output colour. one example of this
252
     * are consoles. they're happy to output colour if talking to a terminal,
253
     * but choose not to output colour if they're only writing to log files
254
     * or to a pipe into another UNIX process.
255
     *
256
     * @return void
257
     */
258
    public function enableColourSupport()
259
    {
260
        foreach ($this->plugins as $plugin)
261
        {
262
            $plugin->enableColourSupport();
263
        }
264
    }
265
266
    /**
267
     * called when storyplayer starts
268
     *
269
     * @param string $version
270
     * @param string $url
271
     * @param string $copyright
272
     * @param string $license
273
     * @return void
274
     */
275
    public function startStoryplayer($version, $url, $copyright, $license)
276
    {
277
        // enforce our inputs
278
        Contract::RequiresValue($version,   is_string($version));
279
        Contract::RequiresValue($url,       is_string($url));
280
        Contract::RequiresValue($copyright, is_string($copyright));
281
        Contract::RequiresValue($license,   is_string($license));
282
283
        // call all of our plugins
284
        foreach ($this->plugins as $plugin)
285
        {
286
            $plugin->startStoryplayer($version, $url, $copyright, $license);
287
        }
288
    }
289
290
    /**
291
     * called when Storyplayer exits
292
     *
293
     * @param  float $duration
294
     *         how long did storyplayer take to run (in seconds)?
295
     * @return int
296
     */
297
    public function endStoryplayer($duration)
298
    {
299
        $retval = 0;
300
301
        foreach ($this->plugins as $plugin)
302
        {
303
            $retval = max($retval, $plugin->endStoryplayer($duration));
304
        }
305
306
        return $retval;
307
    }
308
309
    /**
310
     * called when we start playing a new PhaseGroup
311
     *
312
     * @param  string $activity
313
     *         what are we doing? (e.g. 'creating', 'running')
314
     * @param  string $name
315
     *         the name of the phase group
316
     * @param  array|null $details
317
     *         optional explanation of what this PhaseGroup is trying
318
     *         to achieve
319
     * @return void
320
     */
321
    public function startPhaseGroup($activity, $name, $details = null)
322
    {
323
        // ensure our inputs!
324
        Contract::RequiresValue($activity, is_string($activity));
325
        Contract::RequiresValue($name,     is_string($name));
326
327
        // call our plugins
328
        foreach ($this->plugins as $plugin)
329
        {
330
            $plugin->startPhaseGroup($activity, $name, $details);
331
        }
332
    }
333
334
    /**
335
     * called when we have finished playing a PhaseGroup
336
     *
337
     * NOTE: we cannot use a type-hint for $result here. we may pass in
338
     * a class that inherits from PhaseGroup_Result, and (annoyingly)
339
     * this isn't allowed if we use a type-hint (grrrr)
340
     *
341
     * @param  PhaseGroup_Result $result
342
     * @return void
343
     */
344
    public function endPhaseGroup($result)
345
    {
346
        // enforce our input type
347
        Contract::Requires($result instanceof PhaseGroup_Result);
348
349
        // call our plugins
350
        foreach ($this->plugins as $plugin)
351
        {
352
            $plugin->endPhaseGroup($result);
353
        }
354
    }
355
356
    /**
357
     * called when a story starts a new phase
358
     *
359
     * $param  Phase $phase
360
     *         the phase that we are executing
361
     * @return void
362
     */
363
    public function startPhase($phase)
364
    {
365
        // enforce our input type
366
        Contract::Requires($phase instanceof Phase);
367
368
        foreach ($this->plugins as $plugin)
369
        {
370
            $plugin->startPhase($phase);
371
        }
372
    }
373
374
    /**
375
     * called when a story ends a phase
376
     *
377
     * @param  Phase $phase
378
     *         the phase that has finished
379
     * @param  Phase_Result $phaseResult
380
     *         the result of running $phase
381
     * @return void
382
     */
383
    public function endPhase($phase, $phaseResult)
384
    {
385
        // enforce our input type
386
        Contract::Requires($phase instanceof Phase);
387
        Contract::Requires($phaseResult instanceof Phase_Result);
388
389
        // inject the captured activity into the phase
390
        $phaseResult->activityLog = $this->activityLog;
391
        $this->activityLog=[];
392
393
        // pass the phase on
394
        foreach ($this->plugins as $plugin)
395
        {
396
            $plugin->endPhase($phase, $phaseResult);
397
        }
398
    }
399
400
    /**
401
     * called when a story logs an action
402
     *
403
     * @param string $msg
404
     *        the message to write to the logs / console
405
     * @param array|null $codeLine
406
     *        information about the line of code that is executing
407
     * @return void
408
     */
409 View Code Duplication
    public function logPhaseActivity($msg, $codeLine = null)
410
    {
411
        // enforce our input type
412
        Contract::RequiresValue($msg, is_string($msg));
413
        if ($codeLine) {
414
            Contract::RequiresValue($codeLine, is_array($codeLine));
415
        }
416
417
        // keep track of what was attempted, in case we need to show
418
        // the user what was attempted
419
        $this->activityLog[] = [
420
            'ts'       => time(),
421
            'text'     => $msg,
422
            'codeLine' => $codeLine,
423
            'isOutput' => false,
424
        ];
425
426
        // call all of our plugins
427
        foreach ($this->plugins as $plugin)
428
        {
429
            $plugin->logPhaseActivity($msg, $codeLine);
430
        }
431
    }
432
433
    /**
434
     * called when a story logs the (possibly partial) output from
435
     * running a subprocess
436
     *
437
     * @param  string $msg the output to log
438
     * @return void
439
     */
440
    public function logPhaseSubprocessOutput($msg)
441
    {
442
        // enforce our input type
443
        Contract::RequiresValue($msg, is_string($msg));
444
445
        // keep track of what was attempted, in case we need to show
446
        // the user what was attempted
447
        $this->activityLog[] = [
448
            'ts'       => time(),
449
            'text'     => $msg,
450
            'codeLine' => null,
451
            'isOutput' => true,
452
        ];
453
454
        // call all of our plugins
455
        foreach ($this->plugins as $plugin)
456
        {
457
            $plugin->logPhaseSubprocessOutput($msg);
458
        }
459
    }
460
461
    /**
462
     * called when a story logs an error
463
     *
464
     * @param string $phaseName
465
     *        the name of the phase where the error occurred
466
     * @param string $msg
467
     *        an error message to send to console|logfile
468
     * @return void
469
     */
470 View Code Duplication
    public function logPhaseError($phaseName, $msg)
471
    {
472
        // enforce our inputs
473
        Contract::RequiresValue($phaseName, is_string($phaseName));
474
        Contract::RequiresValue($msg,       is_string($msg));
475
476
        // keep track of what was attempted, in case we need to show
477
        // the user what was attempted
478
        $this->activityLog[] = [
479
            'ts'       => time(),
480
            'text'     => $msg,
481
            'codeLine' => null,
482
        ];
483
484
        // call all of our plugins
485
        foreach ($this->plugins as $plugin)
486
        {
487
            $plugin->logPhaseError($phaseName, $msg);
488
        }
489
    }
490
491
    /**
492
     * called when a story is skipped
493
     *
494
     * @param string $phaseName
495
     *        the name of the phase where the error occurred
496
     * @param string $msg
497
     *        an informational message to send to console|logfile
498
     * @return void
499
     */
500 View Code Duplication
    public function logPhaseSkipped($phaseName, $msg)
501
    {
502
        // enforce our inputs
503
        Contract::RequiresValue($phaseName, is_string($phaseName));
504
        Contract::RequiresValue($msg,       is_string($msg));
505
506
        // keep track of what was attempted, in case we need to show
507
        // the user what was attempted
508
        $this->activityLog[] = [
509
            'ts'       => time(),
510
            'text'     => $msg,
511
            'codeLine' => null,
512
        ];
513
514
        // call all of our plugins
515
        foreach ($this->plugins as $plugin)
516
        {
517
            $plugin->logPhaseSkipped($phaseName, $msg);
518
        }
519
    }
520
521
    /**
522
     * called when we want to record which line of code in a phase is
523
     * currently executing
524
     *
525
     * @param  array $codeLine
526
     *         details about the line of code that is executing
527
     * @return void
528
     */
529
    public function logPhaseCodeLine($codeLine)
530
    {
531
        // enforce our inputs
532
        Contract::RequiresValue($codeLine, is_array($codeLine));
533
534
        // pass it on to all of our plugins
535
        foreach ($this->plugins as $plugin)
536
        {
537
            $plugin->logPhaseCodeLine($codeLine);
538
        }
539
    }
540
541
    /**
542
     * called when the outer CLI shell encounters a fatal error
543
     *
544
     * @param  string $msg
545
     *         the error message to show the user
546
     *
547
     * @return void
548
     */
549
    public function logCliError($msg)
550
    {
551
        // enforce our inputs
552
        Contract::RequiresValue($msg, is_string($msg));
553
554
        // pass it on to our plugins
555
        foreach ($this->plugins as $plugin)
556
        {
557
            $plugin->logCliError($msg);
558
        }
559
    }
560
561
    /**
562
     * called when the outer CLI shell encounters a fatal error
563
     *
564
     * @param  string $msg
565
     *         the error message to show the user
566
     * @param  \Exception $e
567
     *         the exception that caused the error
568
     * @return void
569
     */
570
    public function logCliErrorWithException($msg, $e)
571
    {
572
        // enforce our inputs
573
        Contract::RequiresValue($msg, is_string($msg));
574
        Contract::RequiresValue($e, $e instanceof Exception);
575
576
        // pass this on to our plugins
577
        foreach ($this->plugins as $plugin)
578
        {
579
            $plugin->logCliErrorWithException($msg, $e);
580
        }
581
    }
582
583
    /**
584
     * called when the outer CLI shell needs to publish a warning
585
     *
586
     * @param  string $msg
587
     *         the warning message to show the user
588
     *
589
     * @return void
590
     */
591
    public function logCliWarning($msg)
592
    {
593
        // enforce our inputs
594
        Contract::RequiresValue($msg, is_string($msg));
595
596
        // pass this on to our plugins
597
        foreach ($this->plugins as $plugin)
598
        {
599
            $plugin->logCliWarning($msg);
600
        }
601
    }
602
603
    /**
604
     * called when the outer CLI shell needs to tell the user something
605
     *
606
     * @param  string $msg
607
     *         the message to show the user
608
     *
609
     * @return void
610
     */
611
    public function logCliInfo($msg)
612
    {
613
        // enforce our inputs
614
        Contract::RequiresValue($msg, is_string($msg));
615
616
        // pass this on to our plugins
617
        foreach ($this->plugins as $plugin)
618
        {
619
            $plugin->logCliInfo($msg);
620
        }
621
    }
622
623
    /**
624
     * an alternative to using PHP's built-in var_dump()
625
     *
626
     * @param  string $name
627
     *         a human-readable name to describe $var
628
     *
629
     * @param  mixed $var
630
     *         the variable to dump
631
     *
632
     * @return void
633
     */
634
    public function logVardump($name, $var)
635
    {
636
        // enforce our inputs
637
        Contract::RequiresValue($name, is_string($name));
638
        // $var can be anything, so there is no contract to enforce
639
640
        // pass this on to our plugins
641
        foreach ($this->plugins as $plugin)
642
        {
643
            $plugin->logVardump($name, $var);
644
        }
645
    }
646
}
647