Completed
Pull Request — master (#1574)
by Damian
03:03
created

ModelAsController   B

Complexity

Total Complexity 32

Size/Duplication

Total Lines 148
Duplicated Lines 12.84 %

Coupling/Cohesion

Components 1
Dependencies 15

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 32
lcom 1
cbo 15
dl 19
loc 148
rs 8.8
c 2
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
B controller_for() 0 17 8
A init() 0 4 1
A beforeHandleRequest() 13 14 4
C handleRequest() 6 34 8
D getNestedController() 0 35 9
A find_old_page() 0 8 2

How to fix   Duplicated Code   

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:

1
<?php
2
3
namespace SilverStripe\CMS\Controllers;
4
5
use NestedController;
6
use SilverStripe\ORM\DataModel;
7
use SilverStripe\ORM\DB;
8
use SilverStripe\ORM\DataObject;
9
use Controller;
10
11
use ClassInfo;
12
use Injector;
13
use SS_HTTPRequest;
14
use Director;
15
use RequestHandler;
16
use SS_HTTPResponse;
17
use SS_HTTPResponse_Exception;
18
use Exception;
19
use Translatable;
20
use Debug;
21
use Deprecation;
22
use SilverStripe\CMS\Model\SiteTree;
23
24
25
/**
26
 * ModelAsController deals with mapping the initial request to the first {@link SiteTree}/{@link ContentController}
27
 * pair, which are then used to handle the request.
28
 *
29
 * @package cms
30
 * @subpackage control
31
 */
32
class ModelAsController extends Controller implements NestedController {
33
34
	private static $extensions = array('SilverStripe\\CMS\\Controllers\\OldPageRedirector');
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...
35
36
	/**
37
	 * Get the appropriate {@link ContentController} for handling a {@link SiteTree} object, link it to the object and
38
	 * return it.
39
	 *
40
	 * @param SiteTree $sitetree
41
	 * @param string $action
42
	 * @return ContentController
43
	 */
44
	public static function controller_for(SiteTree $sitetree, $action = null) {
45
		if ($sitetree->class == 'SilverStripe\\CMS\\Model\\SiteTree') {
46
			$controller = "SilverStripe\\CMS\\Controllers\\ContentController";
47
		} else {
48
			$ancestry = ClassInfo::ancestry($sitetree->class);
49
			while ($class = array_pop($ancestry)) {
50
				if (class_exists($class . "_Controller")) break;
51
			}
52
			$controller = ($class !== null) ? "{$class}_Controller" : "SilverStripe\\CMS\\Controllers\\ContentController";
53
		}
54
55
		if($action && class_exists($controller . '_' . ucfirst($action))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $action of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
56
			$controller = $controller . '_' . ucfirst($action);
57
		}
58
59
		return class_exists($controller) ? Injector::inst()->create($controller, $sitetree) : $sitetree;
60
	}
61
62
	public function init() {
63
		singleton('SilverStripe\\CMS\\Model\\SiteTree')->extend('modelascontrollerInit', $this);
64
		parent::init();
65
	}
66
67 View Code Duplication
	protected function beforeHandleRequest(SS_HTTPRequest $request, DataModel $model) {
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...
68
		parent::beforeHandleRequest($request, $model);
69
		// If the database has not yet been created, redirect to the build page.
70
		/** @skipUpgrade */
71
		if(!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
72
			$this->getResponse()->redirect(Controller::join_links(
73
				Director::absoluteBaseURL(),
74
				'dev/build',
75
				'?' . http_build_query(array(
76
					'returnURL' => isset($_GET['url']) ? $_GET['url'] : null,
77
				))
78
			));
79
		}
80
	}
81
82
	/**
83
	 * @uses ModelAsController::getNestedController()
84
	 * @param SS_HTTPRequest $request
85
	 * @param DataModel $model
86
	 * @return SS_HTTPResponse
87
	 */
88
	public function handleRequest(SS_HTTPRequest $request, DataModel $model) {
89
		$this->beforeHandleRequest($request, $model);
90
91
		// If we had a redirection or something, halt processing.
92
		if($this->getResponse()->isFinished()) {
93
			$this->popCurrent();
94
			return $this->getResponse();
95
		}
96
97
		// If the database has not yet been created, redirect to the build page.
98
		/** @skipUpgrade */
99 View Code Duplication
		if(!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
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...
100
			$this->getResponse()->redirect(Director::absoluteBaseURL() . 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null));
101
			$this->popCurrent();
102
103
			return $this->getResponse();
104
		}
105
106
		try {
107
			$result = $this->getNestedController();
108
109
			if($result instanceof RequestHandler) {
110
				$result = $result->handleRequest($this->getRequest(), $model);
111
			} else if(!($result instanceof SS_HTTPResponse)) {
112
				user_error("ModelAsController::getNestedController() returned bad object type '" .
113
					get_class($result)."'", E_USER_WARNING);
114
			}
115
		} catch(SS_HTTPResponse_Exception $responseException) {
116
			$result = $responseException->getResponse();
117
		}
118
119
		$this->popCurrent();
120
		return $result;
121
	}
122
123
	/**
124
	 * @return ContentController
125
	 * @throws Exception If URLSegment not passed in as a request parameter.
126
	 */
127
	public function getNestedController() {
128
		$request = $this->getRequest();
129
130
		if(!$URLSegment = $request->param('URLSegment')) {
131
			throw new Exception('ModelAsController->getNestedController(): was not passed a URLSegment value.');
132
		}
133
134
		// Find page by link, regardless of current locale settings
135
		if(class_exists('Translatable')) Translatable::disable_locale_filter();
136
137
		// Select child page
138
		$conditions = array('"SiteTree"."URLSegment"' => rawurlencode($URLSegment));
139
		if(SiteTree::config()->nested_urls) {
140
			$conditions[] = array('"SiteTree"."ParentID"' => 0);
141
		}
142
		/** @var SiteTree $sitetree */
143
		$sitetree = DataObject::get_one('SilverStripe\\CMS\\Model\\SiteTree', $conditions);
144
145
		// Check translation module
146
		// @todo Refactor out module specific code
147
		if(class_exists('Translatable')) Translatable::enable_locale_filter();
148
149
		if(!$sitetree) {
150
			$this->httpError(404, 'The requested page could not be found.');
151
		}
152
153
		// Enforce current locale setting to the loaded SiteTree object
154
		if(class_exists('Translatable') && $sitetree->Locale) Translatable::set_current_locale($sitetree->Locale);
0 ignored issues
show
Documentation introduced by
The property Locale does not exist on object<SilverStripe\CMS\Model\SiteTree>. 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...
155
156
		if(isset($_REQUEST['debug'])) {
157
			Debug::message("Using record #$sitetree->ID of type $sitetree->class with link {$sitetree->Link()}");
158
		}
159
160
		return self::controller_for($sitetree, $this->getRequest()->param('Action'));
161
	}
162
163
	/**
164
	 * @deprecated 4.0 Use OldPageRedirector::find_old_page instead
165
	 *
166
	 * @param string $URLSegment A subset of the url. i.e in /home/contact/ home and contact are URLSegment.
167
	 * @param int $parent The ID of the parent of the page the URLSegment belongs to.
168
	 * @param bool $ignoreNestedURLs
169
	 * @return SiteTree
170
	 */
171
	static public function find_old_page($URLSegment, $parent = null, $ignoreNestedURLs = false) {
172
		Deprecation::notice('4.0', 'Use OldPageRedirector::find_old_page instead');
173
		if ($parent) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parent of type integer|null 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...
174
			$parent = SiteTree::get()->byID($parent);
175
		}
176
		$url = OldPageRedirector::find_old_page(array($URLSegment), $parent);
0 ignored issues
show
Bug introduced by
It seems like $parent can also be of type integer or object<SilverStripe\ORM\DataObject>; however, SilverStripe\CMS\Control...rector::find_old_page() does only seem to accept object<SilverStripe\CMS\Model\SiteTree>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
177
		return SiteTree::get_by_link($url);
0 ignored issues
show
Bug introduced by
It seems like $url defined by \SilverStripe\CMS\Contro...($URLSegment), $parent) on line 176 can also be of type boolean; however, SilverStripe\CMS\Model\SiteTree::get_by_link() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
178
	}
179
}
180