• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / PHPUnit-Polyfills / 10743946974

06 Sep 2024 07:13PM UTC coverage: 96.423% (+0.6%) from 95.846%
10743946974

push

github

web-flow
Merge pull request #192 from Yoast/feature/3.x/drop-support-for-php-7.0

3.0 | Drop support for PHP < 7.0

28 of 29 new or added lines in 1 file covered. (96.55%)

2 existing lines in 2 files now uncovered.

620 of 643 relevant lines covered (96.42%)

124.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.98
/src/Polyfills/AssertObjectEquals.php
1
<?php
2

3
namespace Yoast\PHPUnitPolyfills\Polyfills;
4

5
use ReflectionNamedType;
6
use ReflectionObject;
7
use ReflectionType;
8
use TypeError;
9
use Yoast\PHPUnitPolyfills\Exceptions\InvalidComparisonMethodException;
10

11
/**
12
 * Polyfill the Assert::assertObjectEquals() methods.
13
 *
14
 * Introduced in PHPUnit 9.4.0.
15
 *
16
 * The polyfill implementation closely matches the PHPUnit native implementation with the exception
17
 * of the thrown exceptions.
18
 *
19
 * @link https://github.com/sebastianbergmann/phpunit/issues/4467
20
 * @link https://github.com/sebastianbergmann/phpunit/issues/4707
21
 * @link https://github.com/sebastianbergmann/phpunit/commit/1dba8c3a4b2dd04a3ff1869f75daaeb6757a14ee
22
 * @link https://github.com/sebastianbergmann/phpunit/commit/6099c5eefccfda860c889f575d58b5fe6cc10c83
23
 */
24
trait AssertObjectEquals {
25

26
        /**
27
         * Asserts that two objects are considered equal based on a custom object comparison
28
         * using a comparator method in the target object.
29
         *
30
         * The custom comparator method is expected to have the following method
31
         * signature: `equals(self $other): bool` (or similar with a different method name).
32
         *
33
         * Basically, the assertion checks the following:
34
         * - A method with name $method must exist on the $actual object.
35
         * - The method must accept exactly one argument and this argument must be required.
36
         * - This parameter must have a classname-based declared type.
37
         * - The $expected object must be compatible with this declared type.
38
         * - The method must have a declared bool return type.
39
         *
40
         * @param object $expected Expected value.
41
         * @param object $actual   The value to test.
42
         * @param string $method   The name of the comparator method within the object.
43
         * @param string $message  Optional failure message to display.
44
         *
45
         * @return void
46
         *
47
         * @throws TypeError                        When any of the passed arguments do not meet the required type.
48
         * @throws InvalidComparisonMethodException When the comparator method does not comply with the requirements.
49
         */
50
        final public static function assertObjectEquals( $expected, $actual, $method = 'equals', $message = '' ) {
345✔
51
                /*
52
                 * Parameter input validation.
53
                 * In PHPUnit this is done via PHP native type declarations. Emulating this for the polyfill.
54
                 */
55
                if ( \is_object( $expected ) === false ) {
345✔
56
                        throw new TypeError(
17✔
57
                                \sprintf(
17✔
58
                                        'Argument 1 passed to assertObjectEquals() must be an object, %s given',
17✔
59
                                        \gettype( $expected )
17✔
60
                                )
3✔
61
                        );
3✔
62
                }
63

64
                if ( \is_object( $actual ) === false ) {
328✔
65
                        throw new TypeError(
17✔
66
                                \sprintf(
17✔
67
                                        'Argument 2 passed to assertObjectEquals() must be an object, %s given',
17✔
68
                                        \gettype( $actual )
17✔
69
                                )
3✔
70
                        );
3✔
71
                }
72

73
                if ( \is_scalar( $method ) === false ) {
311✔
74
                        throw new TypeError(
17✔
75
                                \sprintf(
17✔
76
                                        'Argument 3 passed to assertObjectEquals() must be of the type string, %s given',
17✔
77
                                        \gettype( $method )
17✔
78
                                )
3✔
79
                        );
3✔
80
                }
81
                else {
82
                        $method = (string) $method;
294✔
83
                }
84

85
                /*
86
                 * Comparator method validation.
87
                 */
88
                $reflObject = new ReflectionObject( $actual );
294✔
89

90
                if ( $reflObject->hasMethod( $method ) === false ) {
294✔
91
                        throw new InvalidComparisonMethodException(
17✔
92
                                \sprintf(
17✔
93
                                        'Comparison method %s::%s() does not exist.',
17✔
94
                                        \get_class( $actual ),
17✔
95
                                        $method
17✔
96
                                )
3✔
97
                        );
3✔
98
                }
99

100
                $reflMethod = $reflObject->getMethod( $method );
277✔
101

102
                /*
103
                 * Comparator method return type requirements validation.
104
                 */
105
                $returnTypeError = \sprintf(
277✔
106
                        'Comparison method %s::%s() does not declare bool return type.',
277✔
107
                        \get_class( $actual ),
277✔
108
                        $method
277✔
109
                );
57✔
110

111
                if ( $reflMethod->hasReturnType() === false ) {
277✔
112
                        throw new InvalidComparisonMethodException( $returnTypeError );
17✔
113
                }
114

115
                $returnType = $reflMethod->getReturnType();
260✔
116

117
                if ( \class_exists( 'ReflectionNamedType' ) ) {
260✔
118
                        // PHP >= 7.1: guard against union/intersection return types.
119
                        if ( ( $returnType instanceof ReflectionNamedType ) === false ) {
236✔
120
                                throw new InvalidComparisonMethodException( $returnTypeError );
219✔
121
                        }
122
                }
123
                elseif ( ( $returnType instanceof ReflectionType ) === false ) {
24✔
124
                        /*
125
                         * PHP 7.0.
126
                         * Checking for `ReflectionType` will not throw an error on union types,
127
                         * but then again union types are not supported on PHP 7.0.
128
                         */
NEW
129
                        throw new InvalidComparisonMethodException( $returnTypeError );
×
130
                }
131

132
                if ( $returnType->allowsNull() === true ) {
257✔
133
                        throw new InvalidComparisonMethodException( $returnTypeError );
15✔
134
                }
135

136
                if ( \method_exists( $returnType, 'getName' ) ) {
242✔
137
                        // PHP 7.1+.
138
                        if ( $returnType->getName() !== 'bool' ) {
218✔
139
                                throw new InvalidComparisonMethodException( $returnTypeError );
203✔
140
                        }
141
                }
142
                elseif ( (string) $returnType !== 'bool' ) {
24✔
143
                        // PHP 7.0.
144
                        throw new InvalidComparisonMethodException( $returnTypeError );
2✔
145
                }
146

147
                /*
148
                 * Comparator method parameter requirements validation.
149
                 */
150
                if ( $reflMethod->getNumberOfParameters() !== 1
225✔
151
                        || $reflMethod->getNumberOfRequiredParameters() !== 1
225✔
152
                ) {
153
                        throw new InvalidComparisonMethodException(
32✔
154
                                \sprintf(
32✔
155
                                        'Comparison method %s::%s() does not declare exactly one parameter.',
32✔
156
                                        \get_class( $actual ),
32✔
157
                                        $method
32✔
158
                                )
6✔
159
                        );
6✔
160
                }
161

162
                $noDeclaredTypeError = \sprintf(
193✔
163
                        'Parameter of comparison method %s::%s() does not have a declared type.',
193✔
164
                        \get_class( $actual ),
193✔
165
                        $method
193✔
166
                );
39✔
167

168
                $notAcceptableTypeError = \sprintf(
193✔
169
                        '%s is not an accepted argument type for comparison method %s::%s().',
193✔
170
                        \get_class( $expected ),
193✔
171
                        \get_class( $actual ),
193✔
172
                        $method
193✔
173
                );
39✔
174

175
                $reflParameter = $reflMethod->getParameters()[0];
193✔
176

177
                $hasType = $reflParameter->hasType();
193✔
178
                if ( $hasType === false ) {
193✔
179
                        throw new InvalidComparisonMethodException( $noDeclaredTypeError );
17✔
180
                }
181

182
                $type = $reflParameter->getType();
176✔
183
                if ( \class_exists( 'ReflectionNamedType' ) ) {
176✔
184
                        // PHP >= 7.1.
185
                        if ( ( $type instanceof ReflectionNamedType ) === false ) {
158✔
186
                                throw new InvalidComparisonMethodException( $noDeclaredTypeError );
3✔
187
                        }
188

189
                        $typeName = $type->getName();
155✔
190
                }
191
                else {
192
                        /*
193
                         * PHP 7.0.
194
                         * Checking for `ReflectionType` will not throw an error on union types,
195
                         * but then again union types are not supported on PHP 7.0.
196
                         */
197
                        if ( ( $type instanceof ReflectionType ) === false ) {
18✔
UNCOV
198
                                throw new InvalidComparisonMethodException( $noDeclaredTypeError );
×
199
                        }
200

201
                        $typeName = (string) $type;
18✔
202
                }
203

204
                /*
205
                 * Validate that the $expected object complies with the declared parameter type.
206
                 */
207
                if ( $typeName === 'self' ) {
173✔
208
                        $typeName = \get_class( $actual );
105✔
209
                }
210

211
                if ( ( $expected instanceof $typeName ) === false ) {
173✔
212
                        throw new InvalidComparisonMethodException( $notAcceptableTypeError );
51✔
213
                }
214

215
                /*
216
                 * Execute the comparator method.
217
                 */
218
                $result = $actual->{$method}( $expected );
122✔
219

220
                $msg = \sprintf(
122✔
221
                        'Failed asserting that two objects are equal. The objects are not equal according to %s::%s()',
122✔
222
                        \get_class( $actual ),
122✔
223
                        $method
122✔
224
                );
24✔
225

226
                if ( $message !== '' ) {
122✔
227
                        $msg = $message . \PHP_EOL . $msg;
17✔
228
                }
229

230
                static::assertTrue( $result, $msg );
122✔
231
        }
70✔
232
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc