Completed
Pull Request — master (#25)
by no
04:22 queued 02:07
created

src/ValueValidators/Result.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace ValueValidators;
4
5
/**
6
 * @since 0.1
7
 *
8
 * @license GPL-2.0+
9
 * @author Jeroen De Dauw < [email protected] >
10
 */
11
class Result {
12
13
	/**
14
	 * @since 0.1
15
	 *
16
	 * @var bool
17
	 */
18
	protected $isValid;
19
20
	/**
21
	 * @since 0.1
22
	 *
23
	 * @var Error[]
24
	 */
25
	protected $errors = array();
26
27
	/**
28
	 * @since 0.1
29
	 *
30
	 * @return static
31
	 */
32
	public static function newSuccess() {
33
		return new static( true );
34
	}
35
36
	/**
37
	 * @since 0.1
38
	 *
39
	 * @param Error[] $errors
40
	 *
41
	 * @return static
42
	 */
43
	public static function newError( array $errors ) {
44
		return new static( false, $errors );
45
	}
46
47
	/**
48
	 * Returns a result that represents the combination of the two given results.
49
	 * In particular, this means:
50
	 *
51
	 * If $a->getErrors() is empty and $a->isValid() is true, $b is returned.
52
	 * If $b->getErrors() is empty and $b->isValid() is true, $a is returned.
53
	 *
54
	 * Otherwise, a new Result is constructed that contains
55
	 * all errors from $a and $b, and is considered valid
56
	 * if both $a and $b were valid.
57
	 *
58
	 * @since 0.1
59
	 *
60
	 * @param self $a
61
	 * @param self $b
62
	 *
63
	 * @return self
64
	 */
65
	public static function merge( self $a, self $b ) {
66
		$aErrors = $a->getErrors();
67
		$bErrors = $b->getErrors();
68
69
		if ( $a->isValid() && empty( $aErrors ) ) {
70
			return $b;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $b; (self) is incompatible with the return type documented by ValueValidators\Result::merge of type ValueValidators\Result.

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...
71
		} elseif( $b->isValid() && empty( $bErrors ) ) {
72
			return $a;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $a; (self) is incompatible with the return type documented by ValueValidators\Result::merge of type ValueValidators\Result.

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...
73
		} else {
74
			$errors = array_merge( $aErrors, $bErrors );
75
			$valid = ( $a->isValid() && $b->isValid() );
76
77
			return new self( $valid, $errors );
78
		}
79
	}
80
81
	/**
82
	 * @since 0.1
83
	 *
84
	 * @param bool $isValid
85
	 * @param Error[] $errors
86
	 */
87
	protected function __construct( $isValid, array $errors = array() ) {
88
		$this->isValid = $isValid;
89
		$this->errors = $errors;
90
	}
91
92
	/**
93
	 * Returns if the value was found to be valid or not.
94
	 *
95
	 * @since 0.1
96
	 *
97
	 * @return bool
98
	 */
99
	public function isValid() {
100
		return $this->isValid;
101
	}
102
103
	/**
104
	 * Returns an array with the errors that occurred during validation.
105
	 *
106
	 * @since 0.1
107
	 *
108
	 * @return Error[]
109
	 */
110
	public function getErrors() {
111
		return $this->errors;
112
	}
113
114
}
115