44

I am writing a debug method.

What I have is

if(is_xxx($item)){
 //echo output info for type
}

what I want to do at the end is

if(can_be_string($item))
echo $item;

Is there a can_be_string type function?

Anthony
  • 36,459
  • 25
  • 97
  • 163
Hailwood
  • 89,623
  • 107
  • 270
  • 423
  • @Hailwood the only items I know that cannot become string are objects without `__toString()` method. Where do you get into problems? – eisberg Mar 31 '11 at 07:44
  • What is the determinant of whether something can be a string or not? I don't understand the question. – Jacob Relkin Mar 31 '11 at 07:44
  • I don't believe that a Resource can be cast to string either - not just objects (unless they have __toString) but I think those are the only exceptions – Mark Baker Mar 31 '11 at 07:47
  • yeah, this function has no control over what is plugged into it. so someone could pass in for example a database resource, which could not be converted to a string. – Hailwood Mar 31 '11 at 07:49
  • Add a trap for is_null() as well if you're echoing rather than var_dumping... else you won't see what's echoed – Mark Baker Mar 31 '11 at 08:31
  • @Mark Baker, @Hailwood Resources can be casted as string and will become something like "Resource id #X". – eisberg Mar 31 '11 at 10:50
  • @Hailwood maybe of interest to you: http://www.phpro.org/tutorials/PHP-Type-Casting.html – eisberg Mar 31 '11 at 10:55
  • i suggest you to be clear about what do you mean by 'item'. Based in programming terminology do you mean _array item_ or just any given _value_? Also, you should be clear about what is "can be string", do you mean something that can be concatenated with a string or casted as `(string)`? or also objects with "stringification" methods implementated? – mikl Aug 28 '15 at 14:27

10 Answers10

34

Ok, edited, with incorporating Michiel Pater's suggestion (who's answer is gone now) ans @eisberg's suggestions. settype will return true with objects no matter what, as it seems.

if(
    ( !is_array( $item ) ) &&
    ( ( !is_object( $item ) && settype( $item, 'string' ) !== false ) ||
    ( is_object( $item ) && method_exists( $item, '__toString' ) ) )
)
{
    echo $item;
}
Community
  • 1
  • 1
Decent Dabbler
  • 22,532
  • 8
  • 74
  • 106
  • 1
    Integers, double, float, ... can be casted as well. Arrays too, but without giving any useful information :-) – eisberg Mar 31 '11 at 07:45
  • @eisberg: ugh, you are right of course! Let me check some of my own projects for this bug. LOL. – Decent Dabbler Mar 31 '11 at 07:47
  • @eisberg: How's this then, in your opinion? Good call earlier by the way! – Decent Dabbler Mar 31 '11 at 08:04
  • I would say if I really need to debug anything by hand an not by `var_dump()`, `var_export()`, `print_r()`, ... I would `switch`/`case` the `gettype()`. – eisberg Mar 31 '11 at 10:48
  • 4
    I'm surprised `settype()` doesn't show up in http://me.veekun.com/blog/2012/04/09/php-a-fractal-of-bad-design/ – Adam Monsen Jan 28 '14 at 23:49
  • In PHP 8.0, your check for `__toString` can be replaced with `$item instanceof Stringable` So you could do `settype( $item, 'string' ) !== false || $item instanceof Stringable` I really wish there was something nicer than having to do it all explicitly. – Anthony Aug 11 '21 at 17:28
  • `settype` will raise a PHP notice when it fails. – Tgr Sep 23 '21 at 07:59
34

For the sake of completion...

http://www.php.net/is_scalar, available since PHP 4; not a single word about it.. :)

Roland Franssen
  • 1,038
  • 1
  • 11
  • 22
  • 3
    Some objects can be cast to string. That is if they implement magical method `__toString()`. Also the note says it doesn't consider null value scalar for some reason. On top of that using this function on resources seems unreliable. – Gherman Oct 23 '15 at 07:12
22

How about

function can_be_string($var) {
    return $var === null || is_scalar($var) || (is_object($var) && method_exists($var, '__toString'));
}

And since PHP 8 you can replace the third condition with $var instanceof Stringable

resu
  • 944
  • 12
  • 21
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • 2
    This will return the incorrect result if passed an object with a permissive `__call` function, see: https://3v4l.org/4ttLv. Use `method_exists` instead of `is_callable` – ntzm Feb 03 '17 at 15:55
  • 2
    Also there are no drawbacks of using `method_exists` over `is_callable` since `__toString` must be public – resu Feb 09 '21 at 05:21
10

Quick and dirty implementation with test:

function canBeString($value)
{
    if (is_object($value) and method_exists($value, '__toString')) return true;

    if (is_null($value)) return true;

    return is_scalar($value);
}

class MyClass
{    
}

$object = new MyClass();
var_dump(canBeString($object)); // bool(false)

class MyClassWithToString
{
    public function __toString()
    {
        return 'foo';
    }
}

$objectWithToString = new MyClassWithToString();
var_dump(canBeString($objectWithToString)); // bool(true)

var_dump(canBeString(1)); // bool(true)
echo (string)1 . "\n";

var_dump(canBeString(false)); // bool(true)
echo (string)true . "\n";

var_dump(canBeString(1.0)); // bool(true)
echo (string)1.0 . "\n";

var_dump(canBeString(null)); // bool(false)
var_dump((string)null); // string(0) ""
Maxence
  • 12,868
  • 5
  • 57
  • 69
3

Here is the shortest way I can find:

/**
 * Determine if we can cast a value to a string.
 *
 * @param mixed $v
 *
 * @return bool
 */
function can_be_string($v): bool
{
    return method_exists($v, '__toString') || $v === null || is_scalar($v);
}
ntzm
  • 4,663
  • 2
  • 31
  • 35
2

how about this decision?

function canBeString ($value) {
    try {
        $value = (string) $value;
    } catch (\ErrorException $exception) {
        return false;
    }

    return true;
}
KingPin
  • 1,960
  • 1
  • 12
  • 8
  • 1
    I often go for this type of solution for such cases and up-voted accordingly, though on testing it out, it's a no go as it throws a 'Catchable fatal error' (in PHP 5 at least), which technically is not 'catch'able in the traditional sense for reasons that elude me - see https://stackoverflow.com/questions/2468487/how-can-i-catch-a-catchable-fatal-error-on-php-type-hinting. – John Rix Sep 21 '17 at 11:05
2

It's very simple:

function can_be_string($var)
{
    return method_exists($var, '__toString') || (is_scalar($var) && !is_null($var));
}

The code has already been tested. The method_exists() function returns false if $var is not an object, so this verification is not necessary.

Mirko Pagliai
  • 1,220
  • 1
  • 19
  • 36
1

If is_string() doesn't help you, then I can only suggest some ideas:

  • Trap strlen() or indeed another function that throws an error if the variable is not a string
  • if not NULL, use to_string() and check if the string contains only numeric characters. If not (and the variable/object is a simple variable) you could assume something
Henrik Erlandsson
  • 3,797
  • 5
  • 43
  • 63
0

I didn't think any of the answers were satisfactory.

Simplest implementation:

function can_be_string($value) {
    return (!is_object($value) && !is_array($value))
        || method_exists($value, '__toString');
}

class IsString {
    public function __toString() {
        return 'hello';
    }
}

var_dump(
    can_be_string(null), // true
    can_be_string(123), // true
    can_be_string(12.3), // true
    can_be_string('hello'), // true
    can_be_string(new IsString), // true
    can_be_string(new stdClass), // false
    can_be_string([1, 2, 3]) // false
);
Nicholas Ruunu
  • 603
  • 5
  • 12
-8

With print_r() and var_dump() functions you can print-out contents of any variable.

In case you don't like the output of mentioned functions, there is a plenty of type checking functions here http://www.php.net/manual/en/ref.var.php

jhavrda
  • 403
  • 2
  • 13