Completed
Push — master ( 1a0349...c60a32 )
by Ben
09:05
created

Framework::createValidationError()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
namespace Former\Traits;
3
4
use Former\Interfaces\FrameworkInterface;
5
use HtmlObject\Element;
6
7
/**
8
 * Base helpers and common methods to all frameworks
9
 */
10
abstract class Framework implements FrameworkInterface
11
{
12
	/**
13
	 * The Container
14
	 *
15
	 * @var Container
16
	 */
17
	protected $app;
18
19
	/**
20
	 * Form types that trigger special styling
21
	 *
22
	 * @var array
23
	 */
24
	protected $availableTypes = array();
25
26
	/**
27
	 * The field states available
28
	 *
29
	 * @var array
30
	 */
31
	protected $states = array();
32
33
	/**
34
	 * The default label width (for horizontal forms)
35
	 *
36
	 * @var string
37
	 */
38
	protected $labelWidth;
39
40
	/**
41
	 * The default field width (for horizontal forms)
42
	 *
43
	 * @var string
44
	 */
45
	protected $fieldWidth;
46
47
	/**
48
	 * The default offset for fields (for horizontal form fields
49
	 * with no label, so usually equal to the default label width)
50
	 *
51
	 * @var string
52
	 */
53
	protected $fieldOffset;
54
55
	/**
56
	 * The default HTML tag used for icons
57
	 *
58
	 * @var string
59
	 */
60
	protected $iconTag;
61
62
	/**
63
	 * The default set for icon fonts
64
	 *
65
	 * @var string
66
	 */
67
	protected $iconSet;
68
69
	/**
70
	 * The default prefix icon names
71
	 *
72
	 * @var string
73
	 */
74
	protected $iconPrefix;
75
76
	////////////////////////////////////////////////////////////////////
77
	//////////////////////// CURRENT FRAMEWORK /////////////////////////
78
	////////////////////////////////////////////////////////////////////
79
80
	/**
81
	 * Get the name of the current framework
82
	 *
83
	 * @return string
84
	 */
85
	public function current()
86
	{
87
		return basename(str_replace('\\', '/', get_class($this)));
88
	}
89
90
	/**
91
	 * Check if the current framework matches something
92
	 *
93
	 * @param  string $framework
94
	 *
95
	 * @return boolean
96
	 */
97
	public function is($framework)
98
	{
99
		return $framework == $this->current();
100
	}
101
102
	/**
103
	 * Check if the current framework doesn't match something
104
	 *
105
	 * @param  string $framework
106
	 *
107
	 * @return boolean
108
	 */
109
	public function isnt($framework)
110
	{
111
		return $framework != $this->current();
112
	}
113
114
	////////////////////////////////////////////////////////////////////
115
	/////////////////////////// COMMON METHODS /////////////////////////
116
	////////////////////////////////////////////////////////////////////
117
118
	/**
119
	 * List form types triggered special styling form current framework
120
	 *
121
	 * @return array
122
	 */
123
	public function availableTypes()
124
	{
125
		return $this->availableTypes;
126
	}
127
128
	/**
129
	 * Filter a field state
130
	 *
131
	 * @param string $state
132
	 *
133
	 * @return string
134
	 */
135
	public function filterState($state)
136
	{
137
		// Filter out wrong states
138
		return in_array($state, $this->states) ? $state : null;
139
	}
140
141
	/**
142
	 * Framework error state
143
	 *
144
	 * @return string
145
	 */
146
	public function errorState()
147
	{
148
		return 'error';
149
	}
150
151
	/**
152
	 * Returns corresponding inline class of a field
153
	 *
154
	 * @param Field $field
155
	 *
156
	 * @return string
157
	 */
158
	public function getInlineLabelClass($field)
159
	{
160
		return 'inline';
161
	}
162
163
	/**
164
	 * Set framework defaults from its config file
165
	 */
166
	protected function setFrameworkDefaults()
167
	{
168
		$this->setFieldWidths($this->getFrameworkOption('labelWidths'));
169
		$this->setIconDefaults();
170
	}
171
172
	/**
173
	 * @param string $widths
174
	 */
175
	protected function setFieldWidths($widths)
176
	{
177
	}
178
179
	/**
180
	 * Override framework defaults for icons with config values where set
181
	 */
182
	protected function setIconDefaults()
183
	{
184
		$this->iconTag    = $this->getFrameworkOption('icon.tag');
185
		$this->iconSet    = $this->getFrameworkOption('icon.set');
186
		$this->iconPrefix = $this->getFrameworkOption('icon.prefix');
187
	}
188
189
	/**
190
	 * Render an icon
191
	 *
192
	 * @param array $attributes Its general attributes
193
	 *
194
	 * @return string
195
	 */
196
	public function createIcon($iconType, $attributes = array(), $iconSettings = array())
197
	{
198
		// Check for empty icons
199
		if (!$iconType) {
200
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface Former\Interfaces\FrameworkInterface::createIcon of type string.

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...
201
		}
202
203
		// icon settings can be overridden for a specific icon
204
		$tag    = array_get($iconSettings, 'tag', $this->iconTag);
205
		$set    = array_get($iconSettings, 'set', $this->iconSet);
206
		$prefix = array_get($iconSettings, 'prefix', $this->iconPrefix);
207
208
		return Element::create($tag, null, $attributes)->addClass("$set $prefix-$iconType");
209
	}
210
211
	////////////////////////////////////////////////////////////////////
212
	///////////////////////////// HELPERS //////////////////////////////
213
	////////////////////////////////////////////////////////////////////
214
215
	/**
216
	 * Add classes to a field
217
	 *
218
	 * @param Field $field
219
	 * @param array $classes
220
	 *
221
	 * @return \Former\Traits\Field
222
	 */
223
	protected function addClassesToField($field, $classes)
224
	{
225
		// If we found any class, add them
226
		if ($classes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $classes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
227
			$field->addClass(implode(' ', $classes));
228
		}
229
230
		return $field;
231
	}
232
233
	/**
234
	 * Prepend an array of classes with a string
235
	 *
236
	 * @param array  $classes The classes to prepend
237
	 * @param string $with    The string to prepend them with
238
	 *
239
	 * @return array A prepended array
240
	 */
241
	protected function prependWith($classes, $with)
242
	{
243
		return array_map(function ($class) use ($with) {
244
			return $with.$class;
245
		}, $classes);
246
	}
247
248
	/**
249
	 * Create a label for a field
250
	 *
251
	 * @param Field   $field
252
	 * @param Element $label The field label if non provided
253
	 *
254
	 * @return string A label
255
	 */
256
	public function createLabelOf(Field $field, Element $label = null)
257
	{
258
		// Get the label and its informations
259
		if (!$label) {
260
			$label = $field->getLabel();
261
		}
262
263
		// Get label "for"
264
		$for = $field->id ?: $field->getName();
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Former\Traits\Field>. 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...
265
266
		// Get label text
267
		$text = $label->getValue();
0 ignored issues
show
Bug introduced by
It seems like $label is not always an object, but can also be of type string. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
268
		if (!$text) {
269
			return false;
270
		}
271
272
		// Append required text
273
		if ($field->isRequired()) {
274
			$text .= $this->app['former']->getOption('required_text');
275
		}
276
277
		// Render plain label if checkable, else a classic one
278
		$label->setValue($text);
279
		if (!$field->isCheckable()) {
280
			$label->for($for);
281
		}
282
283
		return $label;
284
	}
285
286
	/**
287
	 * Get an option for the current framework
288
	 *
289
	 * @param string $option
290
	 *
291
	 * @return string
292
	 */
293
	protected function getFrameworkOption($option)
294
	{
295
		return $this->app['config']->get("former.{$this->current()}.$option");
296
	}
297
298
	////////////////////////////////////////////////////////////////////
299
	//////////////////////////// WRAP BLOCKS ///////////////////////////
300
	////////////////////////////////////////////////////////////////////
301
302
	/**
303
	 * Wraps all label contents with potential additional tags.
304
	 *
305
	 * @param  string $label
306
	 *
307
	 * @return string A wrapped label
308
	 */
309
	public function wrapLabel($label)
310
	{
311
		return $label;
312
	}
313
314
	////////////////////////////////////////////////////////////////////
315
	//////////////////////////// RENDER BLOCKS /////////////////////////
316
	////////////////////////////////////////////////////////////////////
317
318
	/**
319
	 * Render an validation error text
320
	 *
321
	 * @param string $text
322
	 * @param array  $attributes
323
	 *
324
	 * @return string
325
	 */
326
	public function createValidationError($text, $attributes = array())
327
	{
328
		return $this->createHelp($text, $attributes);
329
	}
330
}
331