Completed
Push — master ( 331704...66c218 )
by Greg
03:18
created

Run::setDockerEnv()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 2
1
<?php
2
namespace Robo\Task\Docker;
3
4
use Robo\Common\CommandReceiver;
5
6
/**
7
 * Performs `docker run` on a container.
8
 *
9
 * ```php
10
 * <?php
11
 * $this->taskDockerRun('mysql')->run();
12
 *
13
 * $result = $this->taskDockerRun('my_db_image')
14
 *      ->env('DB', 'database_name')
15
 *      ->volume('/path/to/data', '/data')
16
 *      ->detached()
17
 *      ->publish(3306)
18
 *      ->name('my_mysql')
19
 *      ->run();
20
 *
21
 * // retrieve container's cid:
22
 * $this->say("Running container ".$result->getCid());
23
 *
24
 * // execute script inside container
25
 * $result = $this->taskDockerRun('db')
26
 *      ->exec('prepare_test_data.sh')
27
 *      ->run();
28
 *
29
 * $this->taskDockerCommit($result)
30
 *      ->name('test_db')
31
 *      ->run();
32
 *
33
 * // link containers
34
 * $mysql = $this->taskDockerRun('mysql')
35
 *      ->name('wp_db') // important to set name for linked container
36
 *      ->env('MYSQL_ROOT_PASSWORD', '123456')
37
 *      ->run();
38
 *
39
 * $this->taskDockerRun('wordpress')
40
 *      ->link($mysql)
41
 *      ->publish(80, 8080)
42
 *      ->detached()
43
 *      ->run();
44
 *
45
 * ?>
46
 * ```
47
 *
48
 */
49
class Run extends Base
50
{
51
    use CommandReceiver;
52
53
    /**
54
     * @var string
55
     */
56
    protected $image = '';
57
58
    /**
59
     * @var string
60
     */
61
    protected $run = '';
62
63
    /**
64
     * @var string
65
     */
66
    protected $cidFile;
67
68
    /**
69
     * @var string
70
     */
71
    protected $name;
72
73
    /**
74
     * @var string
75
     */
76
    protected $dir;
77
78
    /**
79
     * @param string $image
80
     */
81
    public function __construct($image)
82
    {
83
        $this->image = $image;
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function getPrinted()
90
    {
91
        return $this->isPrinted;
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function getCommand()
98
    {
99
        if ($this->isPrinted) {
100
            $this->option('-i');
101
        }
102
        if ($this->cidFile) {
103
            $this->option('cidfile', $this->cidFile);
104
        }
105
        return trim('docker run ' . $this->arguments . ' ' . $this->image . ' ' . $this->run);
106
    }
107
108
    /**
109
     * @return $this
110
     */
111
    public function detached()
112
    {
113
        $this->option('-d');
114
        return $this;
115
    }
116
117
    /**
118
     * {@inheritdoc)}
119
     */
120
    public function interactive($interactive = true)
121
    {
122
        if ($interactive) {
123
            $this->option('-i');
124
        }
125
        return parent::interactive($interactive);
0 ignored issues
show
Bug Best Practice introduced by
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...
126
    }
127
128
    /**
129
     * @param string|\Robo\Contract\CommandInterface $run
130
     *
131
     * @return $this
132
     */
133
    public function exec($run)
134
    {
135
        $this->run = $this->receiveCommand($run);
136
        return $this;
137
    }
138
139
    /**
140
     * @param string $from
141
     * @param null|string $to
142
     *
143
     * @return $this
144
     */
145
    public function volume($from, $to = null)
146
    {
147
        $volume = $to ? "$from:$to" : $from;
148
        $this->option('-v', $volume);
149
        return $this;
150
    }
151
152
    /**
153
     * Set environment variables.
154
     * n.b. $this->env($variable, $value) also available here,
155
     * inherited from ExecTrait.
156
     *
157
     * @param array $env
158
     * @return type
159
     */
160
    public function envVars(array $env)
161
    {
162
        foreach($env as $variable => $value) {
163
            $this->setDockerEnv($variable, $value);
164
        }
165
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
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...
166
    }
167
168
    /**
169
     * @param string $variable
170
     * @param null|string $value
171
     *
172
     * @return $this
173
     */
174
    protected function setDockerEnv($variable, $value = null)
175
    {
176
        $env = $value ? "$variable=$value" : $variable;
177
        return $this->option("-e", $env);
178
    }
179
180
    /**
181
     * @param null|int $port
182
     * @param null|int $portTo
183
     *
184
     * @return $this
185
     */
186
    public function publish($port = null, $portTo = null)
187
    {
188
        if (!$port) {
0 ignored issues
show
Bug Best Practice introduced by
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...
189
            return $this->option('-P');
190
        }
191
        if ($portTo) {
0 ignored issues
show
Bug Best Practice introduced by
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...
192
            $port = "$port:$portTo";
193
        }
194
        return $this->option('-p', $port);
195
    }
196
197
    /**
198
     * @param string $dir
199
     *
200
     * @return $this
201
     */
202
    public function containerWorkdir($dir)
203
    {
204
        return $this->option('-w', $dir);
205
    }
206
207
    /**
208
     * @param string $user
209
     *
210
     * @return $this
211
     */
212
    public function user($user)
213
    {
214
        return $this->option('-u', $user);
215
    }
216
217
    /**
218
     * @return $this
219
     */
220
    public function privileged()
221
    {
222
        return $this->option('--privileged');
223
    }
224
225
    /**
226
     * @param string $name
227
     *
228
     * @return $this
229
     */
230
    public function name($name)
231
    {
232
        $this->name = $name;
233
        return $this->option('name', $name);
234
    }
235
236
    /**
237
     * @param string|\Robo\Task\Docker\Result $name
238
     * @param string $alias
239
     *
240
     * @return $this
241
     */
242
    public function link($name, $alias)
243
    {
244
        if ($name instanceof Result) {
245
            $name = $name->getContainerName();
246
        }
247
        $this->option('link', "$name:$alias");
248
        return $this;
249
    }
250
251
    /**
252
     * @param string $dir
253
     *
254
     * @return $this
255
     */
256
    public function tmpDir($dir)
257
    {
258
        $this->dir = $dir;
259
        return $this;
260
    }
261
262
    /**
263
     * @return string
264
     */
265
    public function getTmpDir()
266
    {
267
        return $this->dir ? $this->dir : sys_get_temp_dir();
268
    }
269
270
    /**
271
     * @return string
272
     */
273
    public function getUniqId()
274
    {
275
        return uniqid();
276
    }
277
278
    /**
279
     * {@inheritdoc}
280
     */
281
    public function run()
282
    {
283
        $this->cidFile = $this->getTmpDir() . '/docker_' . $this->getUniqId() . '.cid';
284
        $result = parent::run();
285
        $result['cid'] = $this->getCid();
286
        return $result;
287
    }
288
289
    /**
290
     * @return null|string
291
     */
292
    protected function getCid()
293
    {
294
        if (!$this->cidFile || !file_exists($this->cidFile)) {
295
            return null;
296
        }
297
        $cid = trim(file_get_contents($this->cidFile));
298
        @unlink($this->cidFile);
0 ignored issues
show
Security Best Practice introduced by
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...
299
        return $cid;
300
    }
301
}
302