Page MenuHomePhorge

phutil_nonempty_scalar() should never throw an exception with valid scalars (like booleans)
Closed, ResolvedPublic

Description

Preamble

The function phutil_nonempty_scalar() was recently (Apr 20 2022) introduced in Phabricator to help in supporting PHP 8 support and do stricter type checks.

Note that it was introduced since this commit:

https://secure.phabricator.com/rARCf098e8d86373c0651616390a9db6bd215c9bd228

rARCf098e8d86373: Introduce PHP8.1 replacement functions for string tests which may take multiple…

The function is designed to check whenever a scalar can be considered empty or not.

What is a scalar? Uh?

A scalar is a quantity described by a single value. That is.

So, variables containing a single value (or that can be described as a single value) are scalars.

Practical examples:

is_scalar($v)Result
"foo"true
""true
nulltrue
0true
0.5true
truetrue
falsetrue
new stdclass()false
array()false

https://www.php.net/manual/en/function.is-scalar.php

Description of the problem

The results marked in bold are problematic:

Valueis_scalar()phutil_nonempty_scalar()
"foo"truetrue
""truefalse
nulltruefalse
0truetrue
0.5truetrue
truetrueException
falsetrueException
array()falseException
new stdclass()falseException
obj with tostringfalsetrue

I'm not very opinionated about what should happen instead, but I'm 100% sure that an exception is totally wrong in this case, since a boolean is a valid scalar. Full stop.

PHP is already full of absurd things, such as the value zero being considered empty by is_empty(). If we then we throw an exception because a method that is supposed to tell you whether a scalar is empty or not, it throws instead an exception because it receives a valid scalar, that is not acceptable and makes this function virtually not usable.

For booleans the function must not return an exception, but a clean return value.

Proposed solution

The function phutil_nonempty_scalar() is supposed to only check whenever a scalar is empty or not, and it MUST NOT throw an exception if it receives a valid scalar.

Also, it should not declare true as empty, since true is a valid scalar widely considered as non-empty. In fact:

$vis_scalar($v)!is_empty($v)if(strlen($v))(string)$v !== ''(string) $v
truetruetruetruetrue'1'
falsetruefalsefalsefalse''

In short, having said that booleans are scalars:

  • true is non-empty
  • false is empty

So in bold the proposed natural change:

Value...nonempty_scalar()
"foo"true
""false
nullfalse
0true
0.5true
trueException true
falseException false
array()Exception
new stdclass()Exception
object with tostringtrue

Event Timeline

valerio.bozzolan created this task.
valerio.bozzolan created this object in space S1 Public.

I've also a strong opinion about phutil_nonempty_stringlike() that should also not throw an exception on true, but it should consider true as a valid nonempty stringlike since it returns the string 1. And, it should consider false as an empty stringlike.

But this is not inside the scope of this Task.

aklapper renamed this task from phutil_nonempty_scalar() shoud never throw an exception with valid scalars (like booleans) to phutil_nonempty_scalar() should never throw an exception with valid scalars (like booleans).Apr 26 2023, 09:55

By the way the only usages are:

./src/applications/repository/storage/PhabricatorRepository.php:    if (phutil_nonempty_scalar($commit)) {
./src/applications/search/engine/PhabricatorProfileMenuEngine.php:      if (!phutil_nonempty_scalar($item_id)) {
./src/applications/search/field/PhabricatorSearchDateField.php:    if (!phutil_nonempty_scalar($value)) {
./src/applications/search/field/PhabricatorSearchDateField.php:    if (!phutil_nonempty_scalar($value)) {
./src/applications/daemon/event/PhabricatorDaemonEventListener.php:    if (phutil_nonempty_scalar($context)) {