Run   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 254
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 32
lcom 1
cbo 3
dl 0
loc 254
rs 9.84
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A envVars() 0 7 2
A setDockerEnv() 0 5 2
A __construct() 0 4 1
A getPrinted() 0 4 1
A getCommand() 0 10 3
A detached() 0 5 1
A interactive() 0 7 2
A exec() 0 5 1
A volume() 0 6 2
A publish() 0 10 3
A containerWorkdir() 0 4 1
A user() 0 4 1
A privileged() 0 4 1
A name() 0 5 1
A link() 0 8 2
A tmpDir() 0 5 1
A getTmpDir() 0 4 2
A getUniqId() 0 4 1
A run() 0 7 1
A getCid() 0 9 3
1
<?php
2
3
namespace Robo\Task\Docker;
4
5
use Robo\Common\CommandReceiver;
6
7
/**
8
 * Performs `docker run` on a container.
9
 *
10
 * ```php
11
 * <?php
12
 * $this->taskDockerRun('mysql')->run();
13
 *
14
 * $result = $this->taskDockerRun('my_db_image')
15
 *      ->env('DB', 'database_name')
16
 *      ->volume('/path/to/data', '/data')
17
 *      ->detached()
18
 *      ->publish(3306)
19
 *      ->name('my_mysql')
20
 *      ->run();
21
 *
22
 * // retrieve container's cid:
23
 * $this->say("Running container ".$result->getCid());
24
 *
25
 * // execute script inside container
26
 * $result = $this->taskDockerRun('db')
27
 *      ->exec('prepare_test_data.sh')
28
 *      ->run();
29
 *
30
 * $this->taskDockerCommit($result)
31
 *      ->name('test_db')
32
 *      ->run();
33
 *
34
 * // link containers
35
 * $mysql = $this->taskDockerRun('mysql')
36
 *      ->name('wp_db') // important to set name for linked container
37
 *      ->env('MYSQL_ROOT_PASSWORD', '123456')
38
 *      ->run();
39
 *
40
 * $this->taskDockerRun('wordpress')
41
 *      ->link($mysql)
42
 *      ->publish(80, 8080)
43
 *      ->detached()
44
 *      ->run();
45
 *
46
 * ?>
47
 * ```
48
 *
49
 */
50
class Run extends Base
51
{
52
    use CommandReceiver;
53
54
    /**
55
     * @var string
56
     */
57
    protected $image = '';
58
59
    /**
60
     * @var string
61
     */
62
    protected $run = '';
63
64
    /**
65
     * @var string
66
     */
67
    protected $cidFile;
68
69
    /**
70
     * @var string
71
     */
72
    protected $name;
73
74
    /**
75
     * @var string
76
     */
77
    protected $dir;
78
79
    /**
80
     * @param string $image
81
     */
82
    public function __construct($image)
83
    {
84
        $this->image = $image;
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90
    public function getPrinted()
91
    {
92
        return $this->isPrinted;
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98
    public function getCommand()
99
    {
100
        if ($this->isPrinted) {
101
            $this->option('-i');
102
        }
103
        if ($this->cidFile) {
104
            $this->option('cidfile', $this->cidFile);
105
        }
106
        return trim('docker run ' . $this->arguments . ' ' . $this->image . ' ' . $this->run);
107
    }
108
109
    /**
110
     * @return $this
111
     */
112
    public function detached()
113
    {
114
        $this->option('-d');
115
        return $this;
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function interactive($interactive = true)
122
    {
123
        if ($interactive) {
124
            $this->option('-i');
125
        }
126
        return parent::interactive($interactive);
0 ignored issues
show
Bug Best Practice introduced by Greg Anderson
The return type of return parent::interactive($interactive); (Robo\Task\Docker\Run) is incompatible with the return type of the parent method Robo\Task\Docker\Base::interactive of type Robo\Common\ExecTrait.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
127
    }
128
129
    /**
130
     * @param string|\Robo\Contract\CommandInterface $run
131
     *
132
     * @return $this
133
     */
134
    public function exec($run)
135
    {
136
        $this->run = $this->receiveCommand($run);
137
        return $this;
138
    }
139
140
    /**
141
     * @param string $from
142
     * @param null|string $to
143
     *
144
     * @return $this
145
     */
146
    public function volume($from, $to = null)
147
    {
148
        $volume = $to ? "$from:$to" : $from;
149
        $this->option('-v', $volume);
150
        return $this;
151
    }
152
153
    /**
154
     * Set environment variables.
155
     * n.b. $this->env($variable, $value) also available here,
156
     * inherited from ExecTrait.
157
     *
158
     * @param array $env
159
     *
160
     * @return $this
161
     */
162
    public function envVars(array $env)
163
    {
164
        foreach ($env as $variable => $value) {
165
            $this->setDockerEnv($variable, $value);
166
        }
167
        return $this;
0 ignored issues
show
Bug Best Practice introduced by Greg Anderson
The return type of return $this; (Robo\Task\Docker\Run) is incompatible with the return type of the parent method Robo\Task\Docker\Base::envVars of type Robo\Common\ExecTrait.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
168
    }
169
170
    /**
171
     * @param string $variable
172
     * @param null|string $value
173
     *
174
     * @return $this
175
     */
176
    protected function setDockerEnv($variable, $value = null)
177
    {
178
        $env = $value ? "$variable=$value" : $variable;
179
        return $this->option("-e", $env);
180
    }
181
182
    /**
183
     * @param null|int $port
184
     * @param null|int $portTo
185
     *
186
     * @return $this
187
     */
188
    public function publish($port = null, $portTo = null)
189
    {
190
        if (!$port) {
0 ignored issues
show
Bug Best Practice introduced by Davert
The expression $port of type null|integer is loosely compared to false; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
191
            return $this->option('-P');
192
        }
193
        if ($portTo) {
0 ignored issues
show
Bug Best Practice introduced by Davert
The expression $portTo of type null|integer is loosely compared to true; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
194
            $port = "$port:$portTo";
195
        }
196
        return $this->option('-p', $port);
197
    }
198
199
    /**
200
     * @param string $dir
201
     *
202
     * @return $this
203
     */
204
    public function containerWorkdir($dir)
205
    {
206
        return $this->option('-w', $dir);
207
    }
208
209
    /**
210
     * @param string $user
211
     *
212
     * @return $this
213
     */
214
    public function user($user)
215
    {
216
        return $this->option('-u', $user);
217
    }
218
219
    /**
220
     * @return $this
221
     */
222
    public function privileged()
223
    {
224
        return $this->option('--privileged');
225
    }
226
227
    /**
228
     * @param string $name
229
     *
230
     * @return $this
231
     */
232
    public function name($name)
233
    {
234
        $this->name = $name;
235
        return $this->option('name', $name);
236
    }
237
238
    /**
239
     * @param string|\Robo\Task\Docker\Result $name
240
     * @param string $alias
241
     *
242
     * @return $this
243
     */
244
    public function link($name, $alias)
245
    {
246
        if ($name instanceof Result) {
247
            $name = $name->getContainerName();
248
        }
249
        $this->option('link', "$name:$alias");
250
        return $this;
251
    }
252
253
    /**
254
     * @param string $dir
255
     *
256
     * @return $this
257
     */
258
    public function tmpDir($dir)
259
    {
260
        $this->dir = $dir;
261
        return $this;
262
    }
263
264
    /**
265
     * @return string
266
     */
267
    public function getTmpDir()
268
    {
269
        return $this->dir ? $this->dir : sys_get_temp_dir();
270
    }
271
272
    /**
273
     * @return string
274
     */
275
    public function getUniqId()
276
    {
277
        return uniqid();
278
    }
279
280
    /**
281
     * {@inheritdoc}
282
     */
283
    public function run()
284
    {
285
        $this->cidFile = $this->getTmpDir() . '/docker_' . $this->getUniqId() . '.cid';
286
        $result = parent::run();
287
        $result['cid'] = $this->getCid();
288
        return $result;
289
    }
290
291
    /**
292
     * @return null|string
293
     */
294
    protected function getCid()
295
    {
296
        if (!$this->cidFile || !file_exists($this->cidFile)) {
297
            return null;
298
        }
299
        $cid = trim(file_get_contents($this->cidFile));
300
        @unlink($this->cidFile);
0 ignored issues
show
Security Best Practice introduced by Davert
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
301
        return $cid;
302
    }
303
}
304