Index: trunk/phase3/includes/Hooks.php |
— | — | @@ -55,6 +55,7 @@ |
56 | 56 | $data = null; |
57 | 57 | $have_data = false; |
58 | 58 | $closure = false; |
| 59 | + $badhookmsg = false; |
59 | 60 | |
60 | 61 | /* $hook can be: a function, an object, an array of $function and $data, |
61 | 62 | * an array of just a function, an array of object and method, or an |
— | — | @@ -128,10 +129,34 @@ |
129 | 130 | // Run autoloader (workaround for call_user_func_array bug) |
130 | 131 | is_callable( $callback ); |
131 | 132 | |
132 | | - /* Call the hook. */ |
| 133 | + /* Call the hook. The documentation of call_user_func_array clearly |
| 134 | + * states that FALSE is returned on failure. However this is not |
| 135 | + * case always. In some version of PHP if the function signature |
| 136 | + * does not match the call signature, PHP will issue an warning: |
| 137 | + * Param y in x expected to be a reference, value given. |
| 138 | + * |
| 139 | + * In that case the call will also return null. The following code |
| 140 | + * catches that warning and provides better error message. The |
| 141 | + * function documentation also says that: |
| 142 | + * In other words, it does not depend on the function signature |
| 143 | + * whether the parameter is passed by a value or by a reference. |
| 144 | + * There is also PHP bug http://bugs.php.net/bug.php?id=47554 which |
| 145 | + * is unsurprisingly marked as bogus. In short handling of failures |
| 146 | + * with call_user_func_array is a failure, the documentation for that |
| 147 | + * function is wrong and misleading and PHP developers don't see any |
| 148 | + * problem here. |
| 149 | + */ |
| 150 | + $retval = null; |
| 151 | + $handler = set_error_handler( 'hookErrorHandler' ); |
133 | 152 | wfProfileIn( $func ); |
134 | | - $retval = call_user_func_array( $callback, $hook_args ); |
| 153 | + try { |
| 154 | + $retval = call_user_func_array( $callback, $hook_args ); |
| 155 | + } catch ( MWHookException $e ) { |
| 156 | + $badhookmsg = $e->getMessage(); |
| 157 | + } |
135 | 158 | wfProfileOut( $func ); |
| 159 | + // Need to check for null, because set_error_handler borks on it... sigh |
| 160 | + if ( $handler !== null ) set_error_handler( $handler ); |
136 | 161 | |
137 | 162 | /* String return is an error; false return means stop processing. */ |
138 | 163 | |
— | — | @@ -152,9 +177,14 @@ |
153 | 178 | } else { |
154 | 179 | $prettyFunc = strval( $callback ); |
155 | 180 | } |
156 | | - throw new MWException( "Detected bug in an extension! " . |
157 | | - "Hook $prettyFunc failed to return a value; " . |
158 | | - "should return true to continue hook processing or false to abort." ); |
| 181 | + if ( $badhookmsg ) { |
| 182 | + throw new MWException( "Detected bug in an extension! " . |
| 183 | + "Hook $prettyFunc has invalid call signature; " . $badhookmsg ); |
| 184 | + } else { |
| 185 | + throw new MWException( "Detected bug in an extension! " . |
| 186 | + "Hook $prettyFunc failed to return a value; " . |
| 187 | + "should return true to continue hook processing or false to abort." ); |
| 188 | + } |
159 | 189 | } else if ( !$retval ) { |
160 | 190 | return false; |
161 | 191 | } |
— | — | @@ -162,3 +192,12 @@ |
163 | 193 | |
164 | 194 | return true; |
165 | 195 | } |
| 196 | + |
| 197 | +function hookErrorHandler( $errno, $errstr ) { |
| 198 | + if ( strpos( $errstr, 'expected to be a reference, value given' ) !== false ) { |
| 199 | + throw new MWHookException( $errstr ); |
| 200 | + } |
| 201 | + return false; |
| 202 | +} |
| 203 | + |
| 204 | +class MWHookException extends MWException {} |
\ No newline at end of file |