Completed
Push — master ( 166a59...b37844 )
by Stig
07:42 queued 03:48
created

DeployDispatcher::can_abort_deployment()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 2
1
<?php
2
3
/**
4
 * This dispatcher takes care of updating and returning information about this
5
 * projects git repository
6
 */
7
class DeployDispatcher extends Dispatcher {
0 ignored issues
show
Coding Style introduced by
The property $allowed_actions is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $action_types is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
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...
8
9
	const ACTION_DEPLOY = 'deploys';
10
11
	/**
12
	 * @var array
13
	 */
14
	private static $allowed_actions = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
15
		'history',
16
		'show',
17
		'delete',
18
		'log',
19
		'redeploy',
20
		'summary',
21
		'createdeployment',
22
		'start',
23
		'abort'
24
	];
25
26
	private static $dependencies = [
27
		'formatter' => '%$DeploynautAPIFormatter'
28
	];
29
30
	/**
31
	 * @var \DNProject
32
	 */
33
	protected $project = null;
34
35
	/**
36
	 * @var \DNEnvironment
37
	 */
38
	protected $environment = null;
39
40
	/**
41
	 * @var array
42
	 */
43
	private static $action_types = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $action_types is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
44
		self::ACTION_DEPLOY
45
	];
46
47
	/**
48
	 * @param \DNEnvironment $environment
49
	 * @param \Member|null $member
50
	 * @return bool
51
	 */
52
	public static function can_abort_deployment(\DNEnvironment $environment, \Member $member = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $environment is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
53
		if ($member === null) {
54
			$member = \Member::currentUser();
55
		}
56
		return \Permission::checkMember($member, 'ADMIN');
57
	}
58
59 View Code Duplication
	public function init() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
60
		parent::init();
61
62
		$this->project = $this->getCurrentProject();
63
64
		if (!$this->project) {
65
			return $this->project404Response();
66
		}
67
68
		// Performs canView permission check by limiting visible projects
69
		$this->environment = $this->getCurrentEnvironment($this->project);
70
		if (!$this->environment) {
71
			return $this->environment404Response();
72
		}
73
	}
74
75
	/**
76
	 * @param \SS_HTTPRequest $request
77
	 * @return \HTMLText|\SS_HTTPResponse
78
	 */
79
	public function index(\SS_HTTPRequest $request) {
80
		return $this->redirect(\Controller::join_links($this->Link(), 'history'), 302);
81
	}
82
83
	/**
84
	 * @param \SS_HTTPRequest $request
85
	 * @return \SS_HTTPResponse
86
	 */
87
	public function history(\SS_HTTPRequest $request) {
88
		$data = [];
89
90
		$list = $this->environment->DeployHistory('DeployStarted');
91
92
		$fromTimestamp = $request->requestVar('from');
93
		if ($fromTimestamp) {
94
			$from = SS_Datetime::create();
95
			$from->setValue($fromTimestamp);
96
			$list = $list->filter('LastEdited:GreaterThan', $from->Format('Y-m-d H:i:s'));
97
		}
98
99
		foreach ($list as $deployment) {
100
			$data[] = $this->formatter->getDeploymentData($deployment);
0 ignored issues
show
Documentation introduced by
The property formatter does not exist on object<DeployDispatcher>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
101
		}
102
103
		return $this->getAPIResponse([
104
			'list' => $data,
105
		], 200);
106
	}
107
108
	/**
109
	 * @param \SS_HTTPRequest $request
110
	 * @return \SS_HTTPResponse
111
	 */
112
	public function show(\SS_HTTPRequest $request) {
113
		$deployment = \DNDeployment::get()->byId($request->param('ID'));
114
		$errorResponse = $this->validateDeployment($deployment);
0 ignored issues
show
Documentation introduced by
$deployment is of type object<DataObject>|null, but the function expects a object<DNDeployment>.

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...
115
		if ($errorResponse instanceof \SS_HTTPResponse) {
116
			return $errorResponse;
117
		}
118
		return $this->getAPIResponse(['deployment' => $this->formatter->getDeploymentData($deployment)], 200);
0 ignored issues
show
Documentation introduced by
The property formatter does not exist on object<DeployDispatcher>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
119
	}
120
121
	/**
122
	 * @param \SS_HTTPRequest $request
123
	 * @return \SS_HTTPResponse
124
	 */
125
	public function delete(\SS_HTTPRequest $request) {
126
		if ($request->httpMethod() !== 'POST') {
127
			return $this->getAPIResponse(['message' => 'Method not allowed, requires POST'], 405);
128
		}
129
130
		$this->checkSecurityToken();
131
132
		$deployment = \DNDeployment::get()->byId($request->postVar('id'));
133
		$errorResponse = $this->validateDeployment($deployment);
0 ignored issues
show
Documentation introduced by
$deployment is of type object<DataObject>|null, but the function expects a object<DNDeployment>.

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...
134
		if ($errorResponse instanceof \SS_HTTPResponse) {
135
			return $errorResponse;
136
		}
137
138
		try {
139
			$deployment->getMachine()->apply(\DNDeployment::TR_DELETE);
140
		} catch (\Exception $e) {
141
			return $this->getAPIResponse([
142
				'message' => $e->getMessage()
143
			], 400);
144
		}
145
146
		return $this->getAPIResponse([
147
			'deployment' => $this->formatter->getDeploymentData($deployment),
0 ignored issues
show
Documentation introduced by
The property formatter does not exist on object<DeployDispatcher>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
148
			'message' => 'Deployment has been deleted'
149
		], 201);
150
	}
151
152
	/**
153
	 * @param \SS_HTTPRequest $request
154
	 * @return \SS_HTTPResponse
155
	 */
156
	public function log(\SS_HTTPRequest $request) {
157
		$deployment = \DNDeployment::get()->byId($request->param('ID'));
158
		$errorResponse = $this->validateDeployment($deployment);
0 ignored issues
show
Documentation introduced by
$deployment is of type object<DataObject>|null, but the function expects a object<DNDeployment>.

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...
159
		if ($errorResponse instanceof \SS_HTTPResponse) {
160
			return $errorResponse;
161
		}
162
		$log = $deployment->log();
163
		$lines = [];
164
		if ($log->exists()) {
165
			$lines = explode(PHP_EOL, $log->content());
166
		}
167
168
		return $this->getAPIResponse([
169
			'message' => $lines,
170
			'status' => $deployment->Status,
171
			'deployment' => $this->formatter->getDeploymentData($deployment),
0 ignored issues
show
Documentation introduced by
The property formatter does not exist on object<DeployDispatcher>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
172
		], 200);
173
	}
174
175
	/**
176
	 * @param \SS_HTTPRequest $request
177
	 * @return \SS_HTTPResponse
178
	 */
179
	public function redeploy(\SS_HTTPRequest $request) {
180
		$currentBuild = $this->environment->CurrentBuild();
181
		if (!$currentBuild || !$currentBuild->exists()) {
182
			return $this->redirect(Controller::join_links(
183
				$this->environment->Link(\EnvironmentOverview::ACTION_OVERVIEW),
184
				'deployment',
185
				'new'
186
			));
187
		}
188
189
		$strategy = $this->environment->Backend()->planDeploy($this->environment, [
190
			'sha' => $currentBuild->SHA,
191
			'ref_type' => \GitDispatcher::REF_TYPE_PREVIOUS,
192
			'ref_name' => $currentBuild->RefName
193
		]);
194
		$deployment = $strategy->createDeployment();
195
196
		return $this->redirect($deployment->Link());
197
	}
198
199
	/**
200
	 * Return a summary of the deployment changes without creating the deployment.
201
	 *
202
	 * @param \SS_HTTPRequest $request
203
	 * @return \SS_HTTPResponse
204
	 */
205
	public function summary(\SS_HTTPRequest $request) {
206
		if ($request->httpMethod() !== 'POST') {
207
			return $this->getAPIResponse(['message' => 'Method not allowed, requires POST'], 405);
208
		}
209
		$this->checkSecurityToken();
210
211
		$sha = $this->project->resolveRevision($request->postVar('ref'));
212
		if (!$sha) {
213
			return $this->getAPIResponse([
214
				'message' => 'The given reference could not be resolved. Does it exist in the repository?'
215
			], 400);
216
		}
217
218
		$options = ['sha' => $sha];
219 View Code Duplication
		if ($request->postVar('options')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
220
			foreach (explode(',', $request->postVar('options')) as $option) {
221
				$options[$option] = true;
222
			}
223
		}
224
225
		$strategy = $this->createStrategy($options);
226
		return $this->getAPIResponse($strategy->toArray(), 201);
227
	}
228
229
	/**
230
	 * Create deployment. Can't use {@link create()} as it's taken by Object.
231
	 *
232
	 * @param \SS_HTTPRequest $request
233
	 * @return \SS_HTTPResponse
234
	 */
235
	public function createdeployment(\SS_HTTPRequest $request) {
236
		if ($request->httpMethod() !== 'POST') {
237
			return $this->getAPIResponse(['message' => 'Method not allowed, requires POST'], 405);
238
		}
239
240
		$this->checkSecurityToken();
241
242
		// @todo the strategy should have been saved when there has been a request for an
243
		// approval or a bypass. This saved state needs to be checked if it's invalidated
244
		// if another deploy happens before this one
245
246
		$sha = $this->project->resolveRevision($request->postVar('ref'));
247
		if (!$sha) {
248
			return $this->getAPIResponse([
249
				'message' => 'The given reference could not be resolved. Does it exist in the repository?'
250
			], 400);
251
		}
252
253
		// if a ref name was given that is not the sha, then set that. It could be a branch,
254
		// or tag name, or anything else that git uses as a named reference
255
		$refName = null;
256
		if ($request->postVar('ref_name') !== $request->postVar('ref')) {
257
			$refName = $request->postVar('ref_name');
258
		}
259
260
		$options = [
261
			'sha' => $sha,
262
			'ref_type' => $request->postVar('ref_type'),
263
			'ref_name' => $refName,
264
			'title' => $request->postVar('title'),
265
			'summary' => $request->postVar('summary')
266
		];
267
268 View Code Duplication
		if ($request->postVar('options')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
269
			foreach (explode(',', $request->postVar('options')) as $option) {
270
				$options[$option] = true;
271
			}
272
		}
273
274
		$strategy = $this->createStrategy($options);
275
276
		$approver = Member::get()->byId($request->postVar('approver_id'));
277
		if ($approver && $approver->exists()) {
278
			if (!$this->project->allowed(ApprovalsDispatcher::ALLOW_APPROVAL, $approver)) {
0 ignored issues
show
Documentation introduced by
$approver is of type object<DataObject>, but the function expects a object<Member>|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...
279
				return $this->getAPIResponse(['message' => 'The given approver does not have permissions to approve'], 403);
280
			}
281
		}
282
283
		if ($request->postVar('id')) {
284
			$deployment = $strategy->updateDeployment($request->postVar('id'));
285
			$message = 'Deployment has been updated';
286
			$statusCode = 200;
287
		} else {
288
			$deployment = $strategy->createDeployment();
289
			$message = 'Deployment has been created';
290
			$statusCode = 201;
291
		}
292
293
		if ($approver && $approver->exists()) {
294
			$deployment->ApproverID = $approver->ID;
0 ignored issues
show
Documentation introduced by
The property ApproverID does not exist on object<DNDeployment>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
295
			$deployment->write();
296
		}
297
298
		$deploymentLink = \Controller::join_links(Director::absoluteBaseURL(), $deployment->Link());
299
300
		$response = $this->getAPIResponse([
301
			'message' => $message,
302
			'location' => $deploymentLink,
303
			'deployment' => $this->formatter->getDeploymentData($deployment),
0 ignored issues
show
Documentation introduced by
The property formatter does not exist on object<DeployDispatcher>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
304
		], $statusCode);
305
306
		$response->addHeader('Location', $deploymentLink);
307
308
		return $response;
309
	}
310
311
	/**
312
	 * @param \SS_HTTPRequest $request
313
	 * @return \SS_HTTPResponse
314
	 */
315
	public function start(\SS_HTTPRequest $request) {
316
		if ($request->httpMethod() !== 'POST') {
317
			return $this->getAPIResponse(['message' => 'Method not allowed, requires POST'], 405);
318
		}
319
320
		$this->checkSecurityToken();
321
322
		$deployment = \DNDeployment::get()->byId($request->postVar('id'));
323
		$errorResponse = $this->validateDeployment($deployment);
0 ignored issues
show
Documentation introduced by
$deployment is of type object<DataObject>|null, but the function expects a object<DNDeployment>.

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...
324
		if ($errorResponse instanceof \SS_HTTPResponse) {
325
			return $errorResponse;
326
		}
327
328
		// The deployment cannot be started until it has been approved, or bypassed straight to approved state
329
		if ($deployment->State !== \DNDeployment::STATE_APPROVED) {
330
			return $this->getAPIResponse(['message' => 'This deployment has not been approved. Cannot deploy'], 403);
331
		}
332
333
		// until we have a system that can invalidate currently scheduled deployments due
334
		// to emergency deploys etc, replan the deployment to check if it's still valid.
335
		$options = $deployment->getDeploymentStrategy()->getOptions();
336
		$strategy = $this->environment->Backend()->planDeploy($this->environment, $options);
337
		$deployment->Strategy = $strategy->toJSON();
338
339
		// if the person starting is not the one who created the deployment, update the deployer
340
		if (Member::currentUserID() !== $deployment->DeployerID) {
341
			$deployment->DeployerID = Member::currentUserID();
342
		}
343
344
		try {
345
			$deployment->getMachine()->apply(\DNDeployment::TR_QUEUE);
346
		} catch (\Exception $e) {
347
			return $this->getAPIResponse([
348
				'message' => $e->getMessage()
349
			], 400);
350
		}
351
352
		$location = \Controller::join_links(Director::absoluteBaseURL(), $this->Link('log'), $deployment->ID);
353
354
		$response = $this->getAPIResponse([
355
			'message' => 'Deployment has been queued',
356
			'location' => $location,
357
			'deployment' => $this->formatter->getDeploymentData($deployment),
0 ignored issues
show
Documentation introduced by
The property formatter does not exist on object<DeployDispatcher>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
358
		], 201);
359
360
		$response->addHeader('Location', $location);
361
362
		return $response;
363
	}
364
365
	/**
366
	 * @param \SS_HTTPRequest $request
367
	 * @return \SS_HTTPResponse
368
	 */
369
	public function abort(\SS_HTTPRequest $request) {
370
		if ($request->httpMethod() !== 'POST') {
371
			return $this->getAPIResponse(['message' => 'Method not allowed, requires POST'], 405);
372
		}
373
374
		$this->checkSecurityToken();
375
376
		if (!self::can_abort_deployment($this->environment)) {
377
			return $this->getAPIResponse(['message' => 'You are not authorised to perform this action'], 403);
378
		}
379
380
		$deployment = \DNDeployment::get()->byId($request->postVar('id'));
381
		$errorResponse = $this->validateDeployment($deployment);
0 ignored issues
show
Documentation introduced by
$deployment is of type object<DataObject>|null, but the function expects a object<DNDeployment>.

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...
382
		if ($errorResponse instanceof \SS_HTTPResponse) {
383
			return $errorResponse;
384
		}
385
386
		try {
387
			$deployment->getMachine()->apply(\DNDeployment::TR_ABORT);
388
		} catch (\Exception $e) {
389
			return $this->getAPIResponse([
390
				'message' => $e->getMessage()
391
			], 400);
392
		}
393
394
		return $this->sendResponse([
0 ignored issues
show
Documentation introduced by
array('message' => 'Depl...ymentData($deployment)) is of type array<string,?,{"message...ing","deployment":"?"}>, but the function expects a string.

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...
395
			'message' => 'Deployment abort request successfully received',
396
			'deployment' => $this->formatter->getDeploymentData($deployment)
0 ignored issues
show
Documentation introduced by
The property formatter does not exist on object<DeployDispatcher>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
397
		], 200);
398
	}
399
400
	/**
401
	 * @param string $action
402
	 * @return string
403
	 */
404
	public function Link($action = '') {
405
		return \Controller::join_links($this->environment->Link(), self::ACTION_DEPLOY, $action);
406
	}
407
408
	/**
409
	 * @param string $name
410
	 * @return array
411
	 */
412
	public function getModel($name = '') {
413
		return [];
414
	}
415
416
	/**
417
	 * @var array
418
	 * @return \DeploymentStrategy
419
	 */
420
	protected function createStrategy($options) {
421
		$strategy = $this->environment->Backend()->planDeploy($this->environment, $options);
422
		$data = $strategy->toArray();
423
424
		$interface = $this->project->getRepositoryInterface();
425
		if ($interface instanceof \ArrayData && $this->canCompareCodeVersions($interface, $data['changes'])) {
426
			$compareurl = sprintf(
427
				'%s/compare/%s...%s',
428
				$interface->URL,
429
				$data['changes']['Code version']['from'],
430
				$data['changes']['Code version']['to']
431
			);
432
			$data['changes']['Code version']['compareUrl'] = $compareurl;
433
434
			// special case for .platform.yml field so we don't show a huge blob of changes,
435
			// but rather a link to where the .platform.yml changes were made in the code
436
			if (isset($data['changes']['.platform.yml other'])) {
437
				$data['changes']['.platform.yml other']['compareUrl'] = $compareurl;
438
				$data['changes']['.platform.yml other']['description'] = '';
439
			}
440
		}
441
		$this->extend('updateDeploySummary', $data);
442
443
		// Ensure changes that would have been updated are persisted in the object,
444
		// such as the comparison URL, so that they will be written to the Strategy
445
		// field on the DNDeployment object as part of {@link createDeployment()}
446
		$strategy->setChanges($data['changes']);
447
448
		return $strategy;
449
	}
450
451
	/**
452
	 * @param ArrayData $interface
453
	 * @param $changes
454
	 * @return bool
455
	 */
456
	protected function canCompareCodeVersions(\ArrayData $interface, $changes) {
457
		if (empty($changes['Code version'])) {
458
			return false;
459
		}
460
		$codeVersion = $changes['Code version'];
461
		if (empty($interface)) {
462
			return false;
463
		}
464
		if (empty($interface->URL)) {
465
			return false;
466
		}
467
		if (empty($codeVersion['from']) || empty($codeVersion['to'])) {
468
			return false;
469
		}
470
		if (strlen($codeVersion['from']) !== 40 || strlen($codeVersion['to']) !== 40) {
471
			return false;
472
		}
473
		return true;
474
	}
475
476
	/**
477
	 * Check if a DNDeployment exists and do permission checks on it. If there is something wrong it will return
478
	 * an APIResponse with the error, otherwise null.
479
	 *
480
	 * @param \DNDeployment $deployment
481
	 *
482
	 * @return null|SS_HTTPResponse
483
	 */
484 View Code Duplication
	protected function validateDeployment($deployment) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
485
		if (!$deployment || !$deployment->exists()) {
486
			return $this->getAPIResponse(['message' => 'This deployment does not exist'], 404);
487
		}
488
		if ($deployment->EnvironmentID != $this->environment->ID) {
489
			return $this->getAPIResponse(['message' => 'This deployment does not belong to the environment'], 403);
490
		}
491
		if (!$deployment->canView()) {
492
			return $this->getAPIResponse(['message' => 'You are not authorised to view this deployment'], 403);
493
		}
494
		return null;
495
	}
496
497
}
498