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

Yoast / PHPUnit-Polyfills / 12614592068

04 Jan 2025 11:28PM UTC coverage: 96.408% (-1.1%) from 97.504%
12614592068

Pull #221

github

jrfnl
AssertIsTypeTest: remove redundant inline comment
Pull Request #221: AssertIsTypeTest: remove redundant inline comment

671 of 696 relevant lines covered (96.41%)

95.48 hits per line

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

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

3
namespace Yoast\PHPUnitPolyfills\Polyfills;
4

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

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

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

67
                if ( \is_object( $actual ) === false ) {
312✔
68
                        throw new TypeError(
18✔
69
                                \sprintf(
18✔
70
                                        'Argument 2 passed to assertObjectEquals() must be an object, %s given',
18✔
71
                                        \gettype( $actual )
18✔
72
                                )
4✔
73
                        );
4✔
74
                }
75

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

88
                /*
89
                 * Comparator method validation.
90
                 */
91
                $reflObject = new ReflectionObject( $actual );
276✔
92

93
                if ( $reflObject->hasMethod( $method ) === false ) {
276✔
94
                        throw new InvalidComparisonMethodException(
18✔
95
                                \sprintf(
18✔
96
                                        'Comparison method %s::%s() does not exist.',
18✔
97
                                        \get_class( $actual ),
18✔
98
                                        $method
18✔
99
                                )
4✔
100
                        );
4✔
101
                }
102

103
                $reflMethod = $reflObject->getMethod( $method );
258✔
104

105
                /*
106
                 * As the next step, PHPUnit natively would validate the return type,
107
                 * but as return type declarations is a PHP 7.0+ feature, the polyfill
108
                 * skips this check in favour of checking the type of the actual
109
                 * returned value.
110
                 *
111
                 * Also see the upstream discussion about this:
112
                 * {@link https://github.com/sebastianbergmann/phpunit/issues/4707}
113
                 */
114

115
                /*
116
                 * Comparator method parameter requirements validation.
117
                 */
118
                if ( $reflMethod->getNumberOfParameters() !== 1
258✔
119
                        || $reflMethod->getNumberOfRequiredParameters() !== 1
258✔
120
                ) {
121
                        throw new InvalidComparisonMethodException(
34✔
122
                                \sprintf(
34✔
123
                                        'Comparison method %s::%s() does not declare exactly one parameter.',
34✔
124
                                        \get_class( $actual ),
34✔
125
                                        $method
34✔
126
                                )
8✔
127
                        );
8✔
128
                }
129

130
                $noDeclaredTypeError = \sprintf(
224✔
131
                        'Parameter of comparison method %s::%s() does not have a declared type.',
224✔
132
                        \get_class( $actual ),
224✔
133
                        $method
224✔
134
                );
56✔
135

136
                $notAcceptableTypeError = \sprintf(
224✔
137
                        '%s is not an accepted argument type for comparison method %s::%s().',
224✔
138
                        \get_class( $expected ),
224✔
139
                        \get_class( $actual ),
224✔
140
                        $method
224✔
141
                );
56✔
142

143
                $reflParameter = $reflMethod->getParameters()[0];
224✔
144

145
                if ( \method_exists( $reflParameter, 'hasType' ) ) {
224✔
146
                        // PHP >= 7.0.
147
                        $hasType = $reflParameter->hasType();
224✔
148
                        if ( $hasType === false ) {
224✔
149
                                throw new InvalidComparisonMethodException( $noDeclaredTypeError );
18✔
150
                        }
151

152
                        $type = $reflParameter->getType();
206✔
153
                        if ( \class_exists( 'ReflectionNamedType' ) ) {
206✔
154
                                // PHP >= 7.1.
155
                                if ( ( $type instanceof ReflectionNamedType ) === false ) {
186✔
156
                                        throw new InvalidComparisonMethodException( $noDeclaredTypeError );
4✔
157
                                }
158

159
                                $typeName = $type->getName();
182✔
160
                        }
161
                        else {
162
                                /*
163
                                 * PHP 7.0.
164
                                 * Checking for `ReflectionType` will not throw an error on union types,
165
                                 * but then again union types are not supported on PHP 7.0.
166
                                 */
167
                                if ( ( $type instanceof ReflectionType ) === false ) {
20✔
168
                                        throw new InvalidComparisonMethodException( $noDeclaredTypeError );
×
169
                                }
170

171
                                $typeName = (string) $type;
178✔
172
                        }
173
                }
174
                else {
175
                        // PHP < 7.0.
176
                        try {
177
                                /*
178
                                 * Using `ReflectionParameter::getClass()` will trigger an autoload of the class,
179
                                 * but that's okay as for a valid class type that would be triggered on the
180
                                 * function call to the $method (at the end of this assertion) anyway.
181
                                 */
182
                                $hasType = $reflParameter->getClass();
×
183
                        } catch ( ReflectionException $e ) {
×
184
                                // Class with a type declaration for a non-existent class.
185
                                throw new InvalidComparisonMethodException( $notAcceptableTypeError );
×
186
                        }
187

188
                        if ( ( $hasType instanceof ReflectionClass ) === false ) {
×
189
                                // Array or callable type.
190
                                throw new InvalidComparisonMethodException( $noDeclaredTypeError );
×
191
                        }
192

193
                        $typeName = $hasType->name;
×
194
                }
195

196
                /*
197
                 * Validate that the $expected object complies with the declared parameter type.
198
                 */
199
                if ( $typeName === 'self' ) {
202✔
200
                        $typeName = \get_class( $actual );
130✔
201
                }
202

203
                if ( ( $expected instanceof $typeName ) === false ) {
202✔
204
                        throw new InvalidComparisonMethodException( $notAcceptableTypeError );
54✔
205
                }
206

207
                /*
208
                 * Execute the comparator method.
209
                 */
210
                $result = $actual->{$method}( $expected );
148✔
211

212
                if ( \is_bool( $result ) === false ) {
148✔
213
                        throw new InvalidComparisonMethodException(
18✔
214
                                \sprintf(
18✔
215
                                        'Comparison method %s::%s() does not return a boolean value.',
18✔
216
                                        \get_class( $actual ),
18✔
217
                                        $method
18✔
218
                                )
4✔
219
                        );
4✔
220
                }
221

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

228
                if ( $message !== '' ) {
130✔
229
                        $msg = $message . \PHP_EOL . $msg;
18✔
230
                }
231

232
                static::assertTrue( $result, $msg );
130✔
233
        }
70✔
234
}
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