Completed
Pull Request — master (#55)
by Peter
27:03 queued 15:58
created

MultiFormStep::copyValueFromOtherStep()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 6
rs 9.4286
cc 2
eloc 3
nc 2
nop 4
1
<?php
2
3
/**
4
 * MultiFormStep controls the behaviour of a single form step in the MultiForm
5
 * process. All form steps are required to be subclasses of this class, as it
6
 * encapsulates the functionality required for the step to be aware of itself
7
 * in the process by knowing what it's next step is, and if applicable, it's previous
8
 * step.
9
 *
10
 * @package multiform
11
 */
12
class MultiFormStep extends DataObject {
0 ignored issues
show
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...
13
14
	private static $db = array(
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 $db 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...
15
		'Data' => 'Text' // stores serialized maps with all session information
16
	);
17
18
	private static $has_one = array(
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 $has_one 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...
19
		'Session' => 'MultiFormSession'
20
	);
21
22
	/**
23
	 * Centerpiece of the flow control for the form.
24
	 *
25
	 * If set to a string, you have a linear form flow
26
	 * If set to an array, you should use {@link getNextStep()}
27
	 * to enact flow control and branching to different form
28
	 * steps, most likely based on previously set session data
29
	 * (e.g. a checkbox field or a dropdown).
30
	 *
31
	 * @var array|string
32
	 */
33
	public static $next_steps;
34
35
	/**
36
	 * Each {@link MultiForm} subclass needs at least
37
	 * one step which is marked as the "final" one
38
	 * and triggers the {@link MultiForm->finish()}
39
	 * method that wraps up the whole submission.
40
	 *
41
	 * @var boolean
42
	 */
43
	public static $is_final_step = false;
44
45
	/**
46
	 * This variable determines whether a user can use
47
	 * the "back" action from this step.
48
	 *
49
	 * @TODO This does not check if the arbitrarily chosen step
50
	 * using the step indicator is actually a previous step, so
51
	 * unless you remove the link from the indicator template, or
52
	 * type in StepID=23 to the address bar you can still go back
53
	 * using the step indicator.
54
	 *
55
	 * @var boolean
56
	 */
57
	protected static $can_go_back = true;
58
59
	/**
60
	 * Title of this step.
61
	 *
62
	 * Used for the step indicator templates.
63
	 *
64
	 * @var string
65
	 */
66
	protected $title;
67
68
	/**
69
	 * Form class that this step is directly related to.
70
	 *
71
	 * @var MultiForm subclass
72
	 */
73
	protected $form;
74
75
	/**
76
	 * List of additional CSS classes for this step
77
	 *
78
	 * @var array $extraClasses
79
	 */
80
	protected $extraClasses = array();
81
82
	/**
83
	 * Temporary cache to increase the performance for repeated look ups.
84
	 *
85
	 * @var array $cache
86
	 */
87
	protected $cache = array();
88
89
	/**
90
	 * Form fields to be rendered with this step.
91
	 * (Form object is created in {@link MultiForm}.
92
	 *
93
	 * This function needs to be implemented on your
94
	 * subclasses of MultiFormStep.
95
	 *
96
	 * @return FieldList
97
	 */
98
	public function getFields() {
99
		user_error('Please implement getFields on your MultiFormStep subclass', E_USER_ERROR);
100
	}
101
102
	/**
103
	 * Additional form actions to be added to this step.
104
	 * (Form object is created in {@link MultiForm}.
105
	 *
106
	 * Note: This is optional, and is to be implemented
107
	 * on your subclasses of MultiFormStep.
108
	 *
109
	 * @return FieldList
110
	 */
111
	public function getExtraActions() {
112
		return (class_exists('FieldList')) ? new FieldList() : new FieldSet();
0 ignored issues
show
Bug Compatibility introduced by
The expression class_exists('FieldList'...st() : new \FieldSet(); of type FieldList|FieldSet adds the type FieldSet to the return on line 112 which is incompatible with the return type documented by MultiFormStep::getExtraActions of type FieldList.
Loading history...
113
	}
114
115
	/**
116
	 * Get a validator specific to this form.
117
	 * The form is automatically validated in {@link Form->httpSubmission()}.
118
	 *
119
	 * @return Validator
120
	 */
121
	public function getValidator() {
122
		return false;
123
	}
124
125
	/**
126
	 * Accessor method for $this->title
127
	 *
128
	 * @return string Title of this step
129
	 */
130
	public function getTitle() {
131
		return $this->title ? $this->title : $this->class;
132
	}
133
134
	/**
135
	 * Gets a direct link to this step (only works
136
	 * if you're allowed to skip steps, or this step
137
	 * has already been saved to the database
138
	 * for the current {@link MultiFormSession}).
139
	 *
140
	 * @return string Relative URL to this step
141
	 */
142
	public function Link() {
143
		$form = $this->form;
144
		return Controller::join_links($form->getDisplayLink(), "?{$form->config()->get_var}={$this->Session()->Hash}");
0 ignored issues
show
Documentation Bug introduced by
The method Session does not exist on object<MultiFormStep>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
145
	}
146
147
	/**
148
	 * Unserialize stored session data and return it.
149
	 * This is used for loading data previously saved
150
	 * in session back into the form.
151
	 *
152
	 * You need to overload this method onto your own
153
	 * step if you require custom loading. An example
154
	 * would be selective loading specific fields, leaving
155
	 * others that are not required.
156
	 *
157
	 * @return array
158
	 */
159
	public function loadData() {
160
		return ($this->Data && is_string($this->Data)) ? unserialize($this->Data) : array();
0 ignored issues
show
Documentation introduced by
The property Data does not exist on object<MultiFormStep>. 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...
161
	}
162
163
	/**
164
	 * Save the data for this step into session, serializing it first.
165
	 *
166
	 * To selectively save fields, instead of it all, this
167
	 * method would need to be overloaded on your step class.
168
	 *
169
	 * @param array $data The processed data from save() on {@link MultiForm}
170
	 */
171
	public function saveData($data) {
172
		$this->Data = serialize($data);
0 ignored issues
show
Documentation introduced by
The property Data does not exist on object<MultiFormStep>. 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...
173
		$this->write();
174
	}
175
176
	/**
177
	 * Save the data on this step into an object,
178
	 * similiar to {@link Form->saveInto()} - by building
179
	 * a stub form from {@link getFields()}. This is necessary
180
	 * to trigger each {@link FormField->saveInto()} method
181
	 * individually, rather than assuming that all data
182
	 * serialized through {@link saveData()} can be saved
183
	 * as a simple value outside of the original FormField context.
184
	 *
185
	 * @param DataObject $obj
186
	 */
187
	public function saveInto($obj) {
188
		$form = new Form(
189
			Controller::curr(),
190
			'Form',
191
			$this->getFields(),
0 ignored issues
show
Bug introduced by
It seems like $this->getFields() can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
192
			((class_exists('FieldList')) ? new FieldList() : new FieldSet())
193
		);
194
		$form->loadDataFrom($this->loadData());
195
		$form->saveInto($obj);
196
		return $obj;
197
	}
198
199
	/**
200
	 * Custom validation for a step. In most cases, it should be sufficient
201
	 * to have built-in validation through the {@link Validator} class
202
	 * on the {@link getValidator()} method.
203
	 *
204
	 * Use {@link Form->sessionMessage()} to feed back validation messages
205
	 * to the user. Please don't redirect from this method,
206
	 * this is taken care of in {@link next()}.
207
	 *
208
	 * @param array $data Request data
209
	 * @param Form $form
210
	 * @return boolean Validation success
211
	 */
212
	public function validateStep($data, $form) {
0 ignored issues
show
Unused Code introduced by
The parameter $data 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...
Unused Code introduced by
The parameter $form 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...
213
		return true;
214
	}
215
216
	/**
217
	 * Returns the first value of $next_step
218
	 *
219
	 * @return string Classname of a {@link MultiFormStep} subclass
220
	 */
221
	public function getNextStep() {
222
		$nextSteps = static::$next_steps;
223
224
		// Check if next_steps have been implemented properly if not the final step
225
		if(!$this->isFinalStep()) {
226
			if(!isset($nextSteps)) user_error('MultiFormStep->getNextStep(): Please define at least one $next_steps on ' . $this->class, E_USER_ERROR);
227
		}
228
229
		if(is_string($nextSteps)) {
230
			return $nextSteps;
231
		} elseif(is_array($nextSteps) && count($nextSteps)) {
232
			// custom flow control goes here
233
			return $nextSteps[0];
234
		} else {
235
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by MultiFormStep::getNextStep 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...
236
		}
237
	}
238
239
	/**
240
	 * Returns the next step to the current step in the database.
241
	 *
242
	 * This will only return something if you've previously visited
243
	 * the step ahead of the current step, and then gone back a step.
244
	 *
245
	 * @return MultiFormStep|boolean
246
	 */
247
	public function getNextStepFromDatabase() {
248
		if($this->SessionID && is_numeric($this->SessionID)) {
0 ignored issues
show
Documentation introduced by
The property SessionID does not exist on object<MultiFormStep>. 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...
249
			$nextSteps = static::$next_steps;
250
251
			if(is_string($nextSteps)) {
252
				return DataObject::get_one($nextSteps, "\"SessionID\" = {$this->SessionID}");
0 ignored issues
show
Documentation introduced by
The property SessionID does not exist on object<MultiFormStep>. 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...
253
			} elseif(is_array($nextSteps)) {
254
				return DataObject::get_one($nextSteps[0], "\"SessionID\" = {$this->SessionID}");
0 ignored issues
show
Documentation introduced by
The property SessionID does not exist on object<MultiFormStep>. 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...
255
			} else {
256
				return false;
257
			}
258
		}
259
	}
260
261
	/**
262
	 * Accessor method for self::$next_steps
263
	 *
264
	 * @return string|array
265
	 */
266
	public function getNextSteps() {
267
		return static::$next_steps;
268
	}
269
270
	/**
271
	 * Returns the previous step, if there is one.
272
	 *
273
	 * To determine if there is a previous step, we check the database to see if there's
274
	 * a previous step for this multi form session ID.
275
	 *
276
	 * @return string Classname of a {@link MultiFormStep} subclass
277
	 */
278
	public function getPreviousStep() {
279
		$steps = DataObject::get('MultiFormStep', "\"SessionID\" = {$this->SessionID}", '"LastEdited" DESC');
0 ignored issues
show
Documentation introduced by
The property SessionID does not exist on object<MultiFormStep>. 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...
280
		if($steps) {
281
			foreach($steps as $step) {
282
				$step->setForm($this->form);
283
284
				if($step->getNextStep()) {
285
					if($step->getNextStep() == $this->class) {
286
						return $step->class;
287
					}
288
				}
289
			}
290
		}
291
	}
292
293
	/**
294
	 * Retrieves the previous step class record from the database.
295
	 *
296
	 * This will only return a record if you've previously been on the step.
297
	 *
298
	 * @return MultiFormStep subclass
299
	 */
300
	public function getPreviousStepFromDatabase() {
301
		if($prevStepClass = $this->getPreviousStep()) {
302
			return DataObject::get_one($prevStepClass, "\"SessionID\" = {$this->SessionID}");
0 ignored issues
show
Documentation introduced by
The property SessionID does not exist on object<MultiFormStep>. 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...
303
		}
304
	}
305
306
	/**
307
	 * Get the text to the use on the button to the previous step.
308
	 * @return string
309
	 */
310
	public function getPrevText() {
311
		return _t('MultiForm.BACK', 'Back');
312
	}
313
314
	/**
315
	 * Get the text to use on the button to the next step.
316
	 * @return string
317
	 */
318
	public function getNextText() {
319
		return _t('MultiForm.NEXT', 'Next');
320
	}
321
322
	/**
323
	 * Get the text to use on the button to submit the form.
324
	 * @return string
325
	 */
326
	public function getSubmitText() {
327
		return _t('MultiForm.SUBMIT', 'Submit');
328
	}
329
330
	/**
331
	 * Sets the form that this step is directly related to.
332
	 *
333
	 * @param MultiForm subclass $form
334
	 */
335
	public function setForm($form) {
336
		$this->form = $form;
337
	}
338
339
	/**
340
	 * @return Form
341
	 */
342
	public function getForm() {
343
		return $this->form;
344
	}
345
346
	// ##################### Utility ####################
347
348
	/**
349
	 * Determines whether the user is able to go back using the "action_back"
350
	 * Determines whether the user is able to go back using the "action_back"
351
	 * Determines whether the user is able to go back using the "action_back"
352
	 * form action, based on the boolean value of $can_go_back.
353
	 *
354
	 * @return boolean
355
	 */
356
	public function canGoBack() {
357
		return static::$can_go_back;
358
	}
359
360
	/**
361
	 * Determines whether this step is the final step in the multi-step process or not,
362
	 * based on the variable $is_final_step - which must be defined on at least one step.
363
	 *
364
	 * @return boolean
365
	 */
366
	public function isFinalStep() {
367
		return static::$is_final_step;
368
	}
369
370
	/**
371
	 * Determines whether the currently viewed step is the current step set in the session.
372
	 * This assumes you are checking isCurrentStep() against a data record of a MultiFormStep
373
	 * subclass, otherwise it doesn't work. An example of this is using a singleton instance - it won't
374
	 * work because there's no data.
375
	 *
376
	 * @return boolean
377
	 */
378
	public function isCurrentStep() {
379
		return ($this->class == $this->Session()->CurrentStep()->class) ? true : false;
0 ignored issues
show
Documentation Bug introduced by
The method Session does not exist on object<MultiFormStep>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
380
	}
381
382
	/**
383
	 * Add a CSS-class to the step. If needed, multiple classes can be added by delimiting a string with spaces.
384
	 *
385
	 * @param string $class A string containing a classname or several class names delimited by a space.
386
	 * @return MultiFormStep
387
	 */
388 View Code Duplication
	public function addExtraClass($class) {
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...
389
		// split at white space
390
		$classes = preg_split('/\s+/', $class);
391
		foreach($classes as $class) {
392
			// add classes one by one
393
			$this->extraClasses[$class] = $class;
394
		}
395
		return $this;
396
	}
397
398
	/**
399
	 * Remove a CSS-class from the step. Multiple classes names can be passed through as a space delimited string.
400
	 *
401
	 * @param string $class
402
	 * @return MultiFormStep
403
	 */
404 View Code Duplication
	public function removeExtraClass($class) {
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...
405
		// split at white space
406
		$classes = preg_split('/\s+/', $class);
407
		foreach ($classes as $class) {
408
			// unset one by one
409
			unset($this->extraClasses[$class]);
410
		}
411
		return $this;
412
	}
413
414
	/**
415
	 * @return string
416
	 */
417
	public function getExtraClasses() {
418
		return join(' ', array_keys($this->extraClasses));
419
	}
420
421
	/**
422
	 * Returns the submitted value, if any, of any steps.
423
	 *
424
	 * @param string $fromStep (classname)
425
	 * @param string $key
426
	 * @return mixed
427
	 */
428
	public function getValueFromOtherStep($fromStep, $key) {
429
		// load the steps in the cache, if this one doesn't exist
430
		if (!array_key_exists('steps_' . $fromStep, $this->cache)) {
431
			$steps = MultiFormStep::get()->filter('SessionID', $this->form->session->ID);
0 ignored issues
show
Documentation introduced by
The property $session is declared protected in MultiForm. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

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...
432
433
			if($steps) foreach($steps as $step) {
434
				$this->cache['steps_' . $step->ClassName] = $step->loadData();
435
			}
436
		}
437
438
		// check both as PHP isn't recursive
439
		return (isset($this->cache['steps_' . $fromStep]) && isset($this->cache['steps_' . $fromStep][$key])) ?
440
			$this->cache['steps_' . $fromStep][$key] : null;
441
	}
442
443
	/**
444
	 * allows to get a value from another step copied over
445
	 *
446
	 * @param FieldList $fields
447
	 * @param string    $formStep
448
	 * @param string    $fieldName
449
	 * @param string    $fieldNameTarget (optional)
450
	 */
451
	public function copyValueFromOtherStep(FieldList $fields, $formStep, $fieldName, $fieldNameTarget = null) {
452
		// if a target field isn't defined use the same fieldname
453
		if (!$fieldNameTarget) $fieldNameTarget = $fieldName;
0 ignored issues
show
Bug Best Practice introduced by
The expression $fieldNameTarget of type string|null is loosely compared to false; 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...
454
455
		$fields->fieldByName($fieldNameTarget)->setValue($this->getValueFromOtherStep($formStep, $fieldName));
456
	}
457
}
458