Strict mode in PHP
Kind of. You can activate the E_NOTICE
level in your error reporting. (List of constants here.)
Every instance of usage of an undeclared variable will throw an E_NOTICE
.
The E_STRICT
error level will also throw those notices, as well as other hints on how to optimize your code.
error_reporting(E_STRICT);
Terminating the script
If you are really serious, and want your script to terminate instead of just outputting a notice when encountering an undeclared variable, you could build a custom error handler.
A working example that handles only E_NOTICE
s with "Undefined variable" in them and passes everything else on to the default PHP error handler:
<?php
error_reporting(E_STRICT);
function terminate_missing_variables($errno, $errstr, $errfile, $errline)
{
if (($errno == E_NOTICE) and (strstr($errstr, "Undefined variable")))
die ("$errstr in $errfile line $errline");
return false; // Let the PHP error handler handle all the rest
}
$old_error_handler = set_error_handler("terminate_missing_variables");
echo $test; // Will throw custom error
xxxx(); // Will throw standard PHP error
?>
Why is in_array strict mode on integers slower than non-strict mode?
I can offer some small insight from tracing through the C source for in_array
.
It turns out, when comparing integers, the path to reach the actual equality check for non-strict mode involves fewer operations than strict mode.
Strict mode
In the case where the strict
flag to in_array
is true, the following occurs:
We call
fast_is_identical_function
for each element in the arrayThe
fast_is_identical_function
first
tests that the types of each operand are different (Z_TYPE_P(op1) != Z_TYPE_P(op2)
) in hopes of being able to returnfalse
early; this is comparison #1.If the types are the same (they are, in your test case), we then test
(Z_TYPE_P(op1) <= IS_TRUE
; I've no idea what this does, but it's comparison #2.After both comparisons have evaluated to
false
, we jump intozend_is_identical
, our first function invocation.zend_is_identical
starts out by again testingZ_TYPE_P(op1) != Z_TYPE_P(op2)
, another attempt to fail early. This is comparison #3.If the types are the same, we can descend through the
switch (Z_TYPE_P(op1))
statement, comparison #4Finally we reach the comparison
Z_LVAL_P(op1) == Z_LVAL_P(op2)
which actually tests the equality of the two values, comparison #5.
In total, to test whether each element of the array is equal to the value we're searching for, there are 5 comparisons and 1 function invocation.
Non-strict mode
By comparison, the non-strict flow for integers specifically (really LONG
s) is much simpler, as follows:
Instead of
fast_is_identical_function
, we instead usefast_equal_check_function
for each element in the array.The method
fast_equal_check_function
starts a much more complicated process of comparing the two values with all kinds of type-casting logic. However, the very first test it does happens to be optimized for integers, as follows:if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
return Z_LVAL_P(op1) == Z_LVAL_P(op2);We can see that it...
- immediately tests whether type of
op1
isLONG
, which it is, then - immediately tests whether the type of
op2
isLONG
, which it is, then - immediately returns the result of
Z_LVAL_P(op1) == Z_LVAL_P(op2)
- immediately tests whether type of
A total of 3 simple equality comparisons and 0 function invocations for the non-strict case, vs at least 5 comparisons and 1 jump for the strict case.
This appears to be a case where attempted early optimization makes the strict check slower (by repeatedly testing the types of the operands in hopes that we can find inequality sooner) than the specific non-strict case of comparing two integers.
type conversion with strict_types in PHP
It is possible to enable strict mode on a per-file basis. In strict mode, only a variable of exact type of the type declaration will be accepted, or a
TypeError
will be thrown. The only exception to this rule is that an integer may be given to a function expecting a float.http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration.strict
It's allowed as a widening primitive conversion:
A widening primitive conversion does not lose information about the overall magnitude of a numeric value.
Enabling 'strict_types' globally in PHP 7
This is deliberately not possible, because the implementation adopted after an extremely long discussion of scalar type hints was this one: https://wiki.php.net/rfc/scalar_type_hints_v5
It introduces two modes for scalar type parameters, which both guarantee that the function receiving the parameters gets the types that it requires in its signature. However, it provides two modes for how calling code can achieve that:
- in mode 0, it automatically validates and casts certain scalar types (e.g.
int
parameter will convert'123'
to123
, but error on'hello'
) - in mode 1, it requires the caller to do that validation and casting before-hand, and rejects any parameter not of the correct type (e.g. both
'123'
and'hello'
are rejected for anint
parameter)
The choice of mode is per-file, and based on the caller of the function, because:
- the setting needs to affect built-in functions as well as user-defined ones
- all code that calls functions needs to be checked or updated to work correctly in mode 1, but most old code will run fine in mode 0
- with a global setting, you could only use libraries which had been tested with both modes, or the same mode you prefer
- files that don't declare their preferred mode need to continue to work similarly to PHP 5.x to allow existing code to run; that is only possible if the default is mode 0
From the point of view of someone writing a reusable library:
- regardless of the setting, your functions are guaranteed to receive the parameter types requested
- if you want to receive errors when you call functions with the wrong types, you can use mode 1 without forcing other applications and libraries to be on the same setting
- if you want to have the automatic checks and casts of mode 0, you can do that, but still interact with other libraries and applications which run in mode 1
- old libraries which were written before PHP 7.0 (or which needed to support both when it came out) will continue to work roughly as before, because the default mode 0 is similar to existing behaviour for built-in functions
It's therefore up to you to tell PHP which files have been written to use strict type mode, and which haven't; and the way to do this is using the declare
statement.
What does PHPUnit Strict mode do?
Short answer:
for long running tests use an annotation to increase the allowed run time:
@large // 10 seconds
@medium // 5 seconds
@small // 1 second max <-- Default, so no point using
Long Answer:
Here is an updated set of info that was derived with the help of @Crozin.
In my case the error was that a test was taking too long (>1 second.) (Doctrine ORM schema drop + create can slow things down, see this ZendCast for what I was doing). This was causing an issue (and some output) from PHP_Invoker. Strict mode doesnt allow any output.
By Reading / Reverse engineering /usr/share/php/pear/share/pear/PHPUnit/Util/Test.php::getSize() (and getGroups() on the same class) .. I figured out there are 3 undocumented annotations we can use:
@large // 10 seconds
@medium // 5 seconds
@small // 1 second max run time
They can be specified on the class level or on the method level.
Issue #490 on the PHPUnit github hints at issues with supplying both class level and method level so YMMV if you mix them. As crozin said, the allotted time outs are 10,5,1 seconds respectively.
A alternate solution was to increase how long an invoked function is allowed to run (on my slow computer).
sudo vi /usr/share/php/pear/share/pear/PHP/Invoker.php
Increase line 1 "declare(ticks = 1);" to
"declare(ticks = 10);" // or any higher int that meets your needs
Here is a bunch of information about strict mode that helped me find the solution:
PHP_Invoker
A utility class for invoking callables with a timeout. This package is required to enforce test timeouts in strict mode. [PHPUnit Install Instructions]Strict Mode
Tests that do not assert anything are marked as incomplete
Test that are incomplete (or skipped) yield no code coverage Slideshare by Sebastian Bergmann (slide 10)Note
Please note that PHPUnit swallows all output that is emitted during the execution of a test. In strict mode, a test that emits output will fail. Testing output section of PHPUnit Manual
How do you type a mixed parameter in PHP strict mode?
There is no other way to declare a parameter being of "mixed type" (as in, accepting any type of value, be it scalar, reference, a class instance, or even null) than by omitting the type hint.
This says you expect a string
for $key
, an int
for $expiration
; and any value whatsoever for $value
(e.g. 'mixed').
public function set(string $key, $value, int $expiration): bool
declare(strict_type=1)
has no bearing on this. That declaration only has an effect regarding scalar type declarations, where they exist. If there is no type declaration, the type is mixed
, and there can't be any kind of "type coercion", which makes strict_types
moot.
Note that a proposal existed to introduce this pseudo-type declaration, but it never got past the draft stage, since it didn't get a particularly warm reception at the PHP internals group.
Laravel query works with database.php strict=false. How to make it work with strict=true?
GROUP BY
statements in strict mode require that all the non-aggregate fields (COUNT
, SUM
, MAX
, etc.) in the SELECT
statement be present in the GROUP BY
.
As shown in the error, the whereHas()
method has produced a select *
query, however, your GROUP BY
statement only has transaction_id
.
To fix this, you can simply add select('transaction_id')
to your whereHas
call:
->whereHas("allocations", function ($query) {
$query->select('transaction_id')
->havingRaw('transactions.credit > sum(amount)')
->groupBy('transaction_id');
})
Related Topics
Multi-Dimensional Array Post from Form
How to Redirect a 404 Error in a Custom 404 Page Using Codeigniter
How to Send Money to Paypal Using PHP
Change the PHP Path to Mamps PHP
PHP Check If Time Is Between Two Times Regardless of Date
How to Create a Fluent Query Interface
Fatal Error: Allowed Memory Size of 268435456 Bytes Exhausted (Tried to Allocate 71 Bytes)
PHP - Detect Whitespace Between Strings
Does Static Method in PHP Have Any Difference with Non-Static Method
How to Extract a String from Double Quotes
Codeigniter: How to Do a Select (Distinct Fieldname) MySQL Query
Sending Data Using Post in Python to PHP
A _Construct on an Eloquent Laravel Model
Aescrypt Decryption Between iOS and PHP
How to Execute PHP Code in a Sandbox from Within PHP