LangCollectorTask   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 125
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 9
dl 0
loc 125
c 0
b 0
f 0
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 8 4
A run() 0 14 2
B runCollector() 0 26 6
A writeMessage() 0 3 1
A mergeWith() 0 11 4
1
<?php
2
3
/**
4
 * @author    Donatas Navidonskis <[email protected]>
5
 * @since     2017
6
 * @class     LangCollectorTask
7
 *
8
 */
9
class LangCollectorTask extends BuildTask {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
10
11
    protected $title = "Lang Collector Task";
12
13
    protected $description = "Parameters:
14
- module: One or more modules to limit collection (comma-separated)
15
- merge: Merge new strings with existing ones already defined (default: FALSE)
16
- example: /dev/tasks/LangCollectorTask \"module=mysite,themes/default&merge=true\"
17
";
18
19
    /**
20
     * Merge the same created entities if task is running again
21
     *
22
     * @var bool
23
     */
24
    protected $merge = false;
25
26
    /**
27
     * Given modules from the user
28
     *
29
     * @var array
30
     */
31
    protected $module = [];
32
33
    /**
34
     * Text Collector instance to parse an entities
35
     *
36
     * @var i18nTextCollector
37
     */
38
    protected $textCollector;
39
40
    /**
41
     * Initialize within permissions
42
     *
43
     * @return SS_HTTPResponse
44
     */
45
    public function init() {
46
        parent::init();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class BuildTask as the method init() does only exist in the following sub-classes of BuildTask: CleanImageManipulationCache, EncryptAllPasswordsTask, LangCollectorTask, RegenerateCachedImagesTask, i18nTextCollectorTask. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
47
48
        $canAccess = (Director::isDev() || Director::is_cli() || Permission::check("ADMIN"));
49
        if (! $canAccess) {
50
            return Security::permissionFailure($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<LangCollectorTask>, but the function expects a object<Controller>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
51
        }
52
    }
53
54
    public function run($request) {
55
        increase_time_limit_to();
56
57
        $this->writeMessage($this->description);
58
59
        try {
60
            $this->mergeWith($request->getVars());
61
        } catch (LangCollectorException $ex) {
62
            $this->writeMessage($ex->getMessage());
63
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method run() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
64
        }
65
66
        $this->runCollector();
67
    }
68
69
    /**
70
     * Run text collector
71
     *
72
     * @return void
73
     */
74
    protected function runCollector() {
75
        $this->textCollector = new i18nTextCollector();
76
77
        $modules = @$this->textCollector->collect($this->module, $this->merge);
78
79
        foreach ($modules as $moduleName => $entities) {
80
            if (count($entities) <= 0) {
81
                continue;
82
            }
83
84
            // find or create a new module
85
            $module = LangModule::findOrCreate($moduleName);
86
            $this->writeMessage("Collecting module {$module->Name}");
87
88
            foreach ($entities as $namespace => $options) {
89
                $value = $options[0];
90
                $title = isset($options[1]) ? $options[1] : '';
91
92
                $entity = $module->mergeOrAddEntity($namespace, $value, $title, $this->merge);
93
94
                if ($entity instanceof LangEntity) {
95
                    $this->writeMessage("{$namespace} - $value");
96
                }
97
            }
98
        }
99
    }
100
101
    /**
102
     * Write output message
103
     *
104
     * @param string $message
105
     *
106
     * @return void
107
     */
108
    protected function writeMessage($message) {
109
        Debug::message($message, false);
110
    }
111
112
    /**
113
     * Collect parameters from given options array and merge
114
     * it with class properties
115
     *
116
     * @param array $options
117
     *
118
     * @return void
119
     * @throws EmptyModuleException
120
     * @throws InvalidLocaleException
121
     */
122
    protected function mergeWith($options = []) {
123
        if (! array_key_exists('module', $options) || empty($options['module'])) {
124
            throw new EmptyModuleException("Please set one or more (comma-separated) module names");
125
        }
126
127
        $this->module = explode(',', $options['module']);
128
129
        if (array_key_exists('merge', $options)) {
130
            $this->merge = (bool) $options['merge'];
131
        }
132
    }
133
}