Strict Mode in PHP

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_NOTICEs 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:

  1. We call fast_is_identical_function for each element in the array

  2. The 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 return false early; this is comparison #1.

  3. 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.

  4. After both comparisons have evaluated to false, we jump into zend_is_identical, our first function invocation.

  5. zend_is_identical starts out by again testing Z_TYPE_P(op1) != Z_TYPE_P(op2), another attempt to fail early. This is comparison #3.

  6. If the types are the same, we can descend through the switch (Z_TYPE_P(op1)) statement, comparison #4

  7. Finally 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 LONGs) is much simpler, as follows:

  1. Instead of fast_is_identical_function, we instead use fast_equal_check_function for each element in the array.

  2. 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...

    1. immediately tests whether type of op1 is LONG, which it is, then
    2. immediately tests whether the type of op2 is LONG, which it is, then
    3. immediately returns the result of Z_LVAL_P(op1) == Z_LVAL_P(op2)

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' to 123, 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 an int 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



Leave a reply



Submit