Completed
Push — php7-support ( eda8a5...04e19e )
by Sam
06:09
created

DBClassName   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 194
Duplicated Lines 9.28 %

Coupling/Cohesion

Components 1
Dependencies 3
Metric Value
wmc 23
lcom 1
cbo 3
dl 18
loc 194
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A clear_classname_cache() 0 3 1
A __construct() 0 4 1
A requireField() 18 18 1
A getBaseClass() 0 17 4
A setBaseClass() 0 4 1
A getClassNameFromTable() 0 15 4
A getEnum() 0 5 1
B getEnumObsolete() 0 29 6
A setValue() 7 7 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\Model\FieldType;
4
5
use DB;
6
use DataObject;
7
use ClassInfo;
8
9
/**
10
 * Represents a classname selector, which respects obsolete clasess.
11
 *
12
 * @package framework
13
 * @subpackage model
14
 */
15
class DBClassName extends DBEnum {
16
17
	/**
18
	 * Base classname of class to enumerate.
19
	 * If 'DataObject' then all classes are included.
20
	 * If empty, then the baseClass of the parent object will be used
21
	 *
22
	 * @var string|null
23
	 */
24
	protected $baseClass = null;
25
26
	/**
27
	 * Parent object
28
	 *
29
	 * @var DataObject|null
30
	 */
31
	protected $record = null;
32
33
	/**
34
	 * Classname spec cache for obsolete classes. The top level keys are the table, each of which contains
35
	 * nested arrays with keys mapped to field names. The values of the lowest level array are the classnames
36
	 *
37
	 * @var array
38
	 */
39
	protected static $classname_cache = array();
40
41
	/**
42
	 * Clear all cached classname specs. It's necessary to clear all cached subclassed names
43
	 * for any classes if a new class manifest is generated.
44
	 */
45
	public static function clear_classname_cache() {
46
		self::$classname_cache = array();
47
	}
48
49
	/**
50
	 * Create a new DBClassName field
51
	 *
52
	 * @param string $name Name of field
53
	 * @param string|null $baseClass Optional base class to limit selections
54
	 */
55
	public function __construct($name = null, $baseClass = null) {
56
		$this->setBaseClass($baseClass);
57
		parent::__construct($name);
58
	}
59
60
	/**
61
	 * @return void
62
	 */
63 View Code Duplication
	public function requireField() {
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...
64
		$parts = array(
65
			'datatype' => 'enum',
66
			'enums' => $this->getEnumObsolete(),
67
			'character set' => 'utf8',
68
			'collate' => 'utf8_general_ci',
69
			'default' => $this->getDefault(),
70
			'table' => $this->getTable(),
71
			'arrayValue' => $this->arrayValue
72
		);
73
74
		$values = array(
75
			'type' => 'enum',
76
			'parts' => $parts
77
		);
78
79
		DB::require_field($this->getTable(), $this->getName(), $values);
0 ignored issues
show
Documentation introduced by
$values is of type array<string,string|arra...arrayValue\":\"?\"}>"}>, 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...
80
	}
81
82
	/**
83
	 * Get the base dataclass for the list of subclasses
84
	 *
85
	 * @return string
86
	 */
87
	public function getBaseClass() {
88
		// Use explicit base class
89
		if($this->baseClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->baseClass 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...
90
			return $this->baseClass;
91
		}
92
		// Default to the basename of the record
93
		if($this->record) {
94
			return ClassInfo::baseDataClass($this->record);
95
		}
96
		// During dev/build only the table is assigned
97
		$tableClass = $this->getClassNameFromTable($this->getTable());
98
		if($tableClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $tableClass 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...
99
			return $tableClass;
100
		}
101
		// Fallback to global default
102
		return 'DataObject';
103
	}
104
105
	/**
106
	 * Assign the base class
107
	 *
108
	 * @param string $baseClass
109
	 * @return $this
110
	 */
111
	public function setBaseClass($baseClass) {
112
		$this->baseClass = $baseClass;
113
		return $this;
114
	}
115
116
	/**
117
	 * Given a table name, find the base data class
118
	 *
119
	 * @param string $table
120
	 * @return string|null
121
	 */
122
	protected function getClassNameFromTable($table) {
123
		if(empty($table)) {
124
			return null;
125
		}
126
		$class = ClassInfo::baseDataClass($table);
127
		if($class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class of type null|string 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...
128
			return $class;
129
		}
130
		// If there is no class for this table, strip table modifiers (_Live / _versions) off the end
131
		if(preg_match('/^(?<class>.+)(_[^_]+)$/i', $this->getTable(), $matches)) {
132
			return $this->getClassNameFromTable($matches['class']);
133
		}
134
		
135
		return null;
136
	}
137
138
	/**
139
	 * Get list of classnames that should be selectable
140
	 *
141
	 * @return array
142
	 */
143
	public function getEnum() {
144
		$classNames = ClassInfo::subclassesFor($this->getBaseClass());
145
		unset($classNames['DataObject']);
146
		return $classNames;
147
	}
148
149
	/**
150
	 * Get the list of classnames, including obsolete classes.
151
	 *
152
	 * If table or name are not set, or if it is not a valid field on the given table,
153
	 * then only known classnames are returned.
154
	 *
155
	 * Values cached in this method can be cleared via `DBClassName::clear_classname_cache();`
156
	 *
157
	 * @return array
158
	 */
159
	public function getEnumObsolete() {
160
		// Without a table or field specified, we can only retrieve known classes
161
		$table = $this->getTable();
162
		$name = $this->getName();
163
		if(empty($table) || empty($name)) {
164
			return $this->getEnum();
165
		}
166
167
		// Ensure the table level cache exists
168
		if(empty(self::$classname_cache[$table])) {
169
			self::$classname_cache[$table] = array();
170
		}
171
		
172
		// Check existing cache
173
		if(!empty(self::$classname_cache[$table][$name])) {
174
			return self::$classname_cache[$table][$name];
175
		}
176
		
177
		// Get all class names
178
		$classNames = $this->getEnum();
179
		if(DB::get_schema()->hasField($table, $name)) {
180
			$existing = DB::query("SELECT DISTINCT \"{$name}\" FROM \"{$table}\"")->column();
181
			$classNames = array_unique(array_merge($classNames, $existing));
182
		}
183
184
		// Cache and return
185
		self::$classname_cache[$table][$name] = $classNames;
186
		return $classNames;
187
	}
188
189 View Code Duplication
	public function setValue($value, $record = null, $markChanged = true) {
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...
190
		parent::setValue($value, $record, $markChanged);
191
192
		if($record instanceof DataObject) {
193
			$this->record = $record;
194
		}
195
	}
196
197
	public function getDefault() {
198
		// Check for assigned default
199
		$default = parent::getDefault();
200
		if($default) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $default 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...
201
			return $default;
202
		}
203
		
204
		// Fallback to first option
205
		$enum = $this->getEnum();
206
		return reset($enum);
207
	}
208
}
209