pc579's avatar
Level 1

Why __toString is called ?

How can we explain that the magic fonction __toString of « vendor/symfony/http-foundation/Request.php » is called at the end of « vendor/laravel/framework/src/Illuminate/Http/Request.php / createFromBase ». I see nowhere $request cast to string/call to tostring function.

PS : this question is linked to this one https://laracasts.com/discuss/channels/laravel/request-question

0 likes
11 replies
pc579's avatar
Level 1

Thanks @wew for your reply. My update on the other post was not enough clear, I edited it, the Laravel version for the projects are the same 9.26.1.

"It isn't cast", I know, it's not probably the right term but I don't use (dont't see where) I use the toString function so __toString should not be called ...

Tray2's avatar

Ok, let's look at some code.

class SomeNiceClass()
{
	public function getResult()
	{
		return $this->processData();
	}

	protected function processDate()
	{
			//Do processing here
	}
}

$class = new SomeNiceClass();

echo $class->getResult();

If you have a __toString method you can then make the code a little cleaner.

class SomeNiceClass()
{
	public function __toString()
	{
		return $this->getResult();
	}
	public function getResult()
	{
		return $this->processData();
	}

	protected function processDate()
	{
			//Do processing here
	}
}

$class = new SomeNiceClass();

echo $class;

so what it basically does is allow you to perform a custom action when php ask it to automagically cast it to a string, like when you want to echo the class.

You can do the same thing when you want to the result to be an array by using the magic __to_array() method.

All the __methods work a bit like the constructor, php calls it on it's own if the circumstances fits the method.

1 like
pc579's avatar
Level 1

Thanks @tray2 for your reply, but the question is not what's the use of __toString but why/where it is called?

Tray2's avatar

@pc579 it is called automagically, just like the constructor method in a class is called, or the invoke method in a single action controller.

The code below instatiates a new class, and since we are passing an argument, we need to have a constructor to handle that.

$class = new MyClass('MyFirstClass');

That means that php will look for a method named __construct() and run it. A class doesn't require a constructor, but if you want to set properties when you instansciate your class, then you use one.

The same thing goes for an invokable class, that means that you invoke the class when you instantiate it. Take this ontroller as an example, it uses both a constructor and an invoke to apply the middleware and return the view.

class BooksCreateController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }

    public function __invoke()
    {
        return view('books.create')
	}
}

So back to the __toString() method, it is called when php needs a string, and the class method doesn't return one, that is of course if you have specified the method.

class MyClass
{
		protected string $name;

		public function setName($name)
		{
				$this->name = $name;
		}

		public function getName()
		{
				return $this->name;
		}
}

The class above has two methods, a getter and a setter.

If I do this in my code.

$class = new MyClass();
$class->setName('Tray2');
echo $class->getName();

It will echo Tray2.

Since I only have one method returning somthing, then I can simplify the code by doing this

$class = new MyClass();
$class->setName('Tray2');
echo $class;

If I do as above, php will then try to echo $class this will fail, since it is an object of the type MyClass, but php is smart enough to look for a way to try to convert the $class into a string, and what it looks for is __toString() and since we don't have one, it will fail.

To prevent that from happening, we add that method to our class, then the developer can choose if they want to use the verbose getter or just use the magic casting the object to a string with __toString()

class MyClass
{
		protected string $name;

		public function __toString()
		{
				return $this->getName();
		}

		public function setName($name)
		{
				$this->name = $name;
		}

		public function getName()
		{
				return $this->name;
		}
}

So to put it simple, php looks for it when it needs to cast an instance of the class to a string value, and if it exists, it does it automagically, so you as a developer doesn't need to write a getter for a proptery, like in the example above.

2 likes
Sinnbeck's avatar

It gets called here

https://github.com/symfony/http-foundation/blob/6.3/Response.php#L442

Symfony tells php that it wants a string, to php will convert it to a string (or try to at least)

A simple example

    $func = function (string $foo) {
        return $foo;
    };

    $class = new class {
        public function __toString(): string
        {
            return 'foooo';
        }
    };
    var_dump($func($class)); exit; //shows string(5) "foooo"
1 like
pc579's avatar
Level 1

Thanks @tray2 for your very detailed answer.

Thanks @sinnbeck for your reply that I don't understand.

As I wrote in my initial post __toString is called by vendor/symfony/http-foundation/Request.php

that is called by vendor/laravel/framework/src/Illuminate/Http/Request.php / createFromBase

    public static function createFromBase(SymfonyRequest $request)
    {
        $newRequest = (new static)->duplicate(
            $request->query->all(), $request->request->all(), $request->attributes->all(),
            $request->cookies->all(), $request->files->all(), $request->server->all()
        );

        $newRequest->headers->replace($request->headers->all());

        $newRequest->content = $request->content;

        if ($newRequest->isJson()) {
            $newRequest->request = $newRequest->json();
        }

        return $newRequest;
    }

and I don't see in these lines why __toString is callled?

Tray2's avatar
Tray2
Best Answer
Level 73

@pc579 Why do you even care, does it cause any errors, or are you just trying to understand what the framework does in each nitty grítty detail?

Take a look at this example..

Class One
{
  public function __toString()
  {
    return 'Tjohoo';
  }
}

Class Two extends One
{
  public function doSomeThing()
  {
    return 'did something';
  }
}


$class = new Two();

echo $class;

This will call the __toString method in the parent class, that means that it isn't necessarily the createFromBase that generates the calling.

1 like
pc579's avatar
Level 1

@Tray2 that means that it isn't necessarily the createFromBase that generates

Right, I had a "debug echo in a file" to debug my previous post (not critical just annoying bug that means that I created this debug some months ago and forgot to remove it)

Tray2's avatar

@pc579 That is correct. Glad you found the problem and solved it.

pc579's avatar
Level 1

Many thanks for you both for your answers, I have to do my own examples with your clues to better understand. It'll take a (little) time , my brain is boiling hot right now :-)

Please or to participate in this conversation.