Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug, because it leads easily to a potential broken site.
Issue priority Major, because its quite an odd and hard to debug problem
Disruption I hope no PHP code relied on the magic conversion. Twig will continue to work

Problem/Motivation

You cannot throw exceptions in __toString() methods, because PHP will fatal
if you do that, see http://3v4l.org/Y33D8

Currently Url::toString()throws though an exception (route not found for example), so we potentially fatal if we have somehow created an invalid URL,
which might happen somehow.

Proposed resolution

catch the exceptions?

Remaining tasks

User interface changes

API changes

We remove the support for the magic conversions of the URL object in code, but we keep the functionality in twig.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

kgoel’s picture

Issue tags: +D8 Accelerate NJ
kgoel’s picture

working on this

pwolanin’s picture

Status: Active » Needs review
Parent issue: » #2407505: [meta] Finalize the menu links (and other user-entered paths) system
FileSize
559 bytes

Like this?

dawehner’s picture

+++ b/core/lib/Drupal/Core/Url.php
@@ -643,7 +643,14 @@ public function toString() {
+      // PHP does not allow an exception to be thrown from within __toString()
+      // so make sure we suppress them all.
+      return '';

Instead of returning an empty string I would rather vote for returning something which actually helps people to debug that. Maybe a watchdog entry (which also potentially throws an exception) or simple return 'invalid-url', or similar.

pwolanin’s picture

How about the exception class as the return string?

pwolanin’s picture

Well, actually - what's the use case for __toString? I accidentally cast to string? or we want to use it somplace?

neclimdul’s picture

I think this is for Twig?

dawehner’s picture

As talked in IRC with @pwolanin we could also change twig_render_var() to check for a toString() method and use that instead.
When we decide to do that I would vote to drop the magic string support again, its error prone.

dawehner’s picture

FileSize
5.85 KB

Let's implement the idea in twig.

For php code we should not try to randomly cast objects to strings, this is dangerous in terms of potential bugs anyway.

Status: Needs review » Needs work

The last submitted patch, 9: 2416971-9.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
7.4 KB
1.55 KB

I hope these are the places of the magic function being used.

Status: Needs review » Needs work

The last submitted patch, 11: 2416971-11.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
7.25 KB
1.05 KB

Fixed in a more sane way.

Status: Needs review » Needs work

The last submitted patch, 13: 2416971-13.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
9.21 KB
2.19 KB

Some more fixes.

Status: Needs review » Needs work

The last submitted patch, 15: 2416971-15.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
10.78 KB
2.3 KB

Some more small fixes.

dawehner’s picture

Issue summary: View changes
Wim Leers’s picture

This greatly benefits the DX & TX, by not having puzzling fatals in Twig anymore. I'd RTBC, but I have a docs question. Once that's answered/handled, RTBC.

  1. +++ b/core/modules/system/src/Tests/Theme/EngineTwigTest.php
    @@ -84,6 +84,25 @@ public function testTwigLinkGenerator() {
    +   * Tests the magic url to string Twig functions.
    

    Could be clearer, e.g. Tests the automatic/magic calling of a <code>toString() method, if it exists, by Twig.

  2. +++ b/core/themes/engines/twig/twig.engine
    @@ -150,6 +150,9 @@ function twig_render_var($arg) {
    +    elseif (method_exists($arg, 'toString')) {
    +      return $arg->toString();
    +    }
    
    @@ -237,6 +240,9 @@ function twig_drupal_escape_filter(\Twig_Environment $env, $string, $strategy =
    +    elseif (method_exists($string, 'toString')) {
    +      $return = $string->toString();
    +    }
    

    Perhaps document here why Twig also supports toString(), which seems weird/non-standard, but is done for good reasons. Perhaps link to that 3v4l result, or if possible, to PHP docs?

Wim Leers’s picture

Title: Url::__toString() should catch exceptions and return '' » Remove Url::__toString()

More accurate title since the course was apparently changed midway.

dawehner’s picture

FileSize
11.29 KB
1.63 KB

Alright, fixed those points, hopefully.

dawehner’s picture

FileSize
11.29 KB

Just a simple rebase.

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +PHPWTF

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 22: 2416971-22.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
13.13 KB
1.84 KB

This could be it.

Status: Needs review » Needs work

The last submitted patch, 25: 2416971-25.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
13.15 KB
1.25 KB

More fixes.

pwolanin’s picture

Looks reasonable, though it's a little sad to lose the magic.

is there a method name that we use elsewhere or that's common for this? I guess toSting() is intuitive enough.

dawehner’s picture

Given that we use $url->toString() as official API everywhere in much more places, I think that method is fine.

dawehner’s picture

Issue summary: View changes
neclimdul’s picture

Status: Needs review » Reviewed & tested by the community

lets RTBC again. Still looks good.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 27: 2416971-27.patch, failed testing.

dawehner’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
13.09 KB

Just a simple reroll.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 33: 2416971-33.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
13.31 KB
1.91 KB

There we go.

mpdonadio’s picture

+++ b/core/modules/views_ui/src/ViewUI.php
@@ -726,7 +726,7 @@ public function renderPreview($display_id, $args = array()) {
             if (isset($path)) {
               // @todo Views should expect and store a leading /. See:
               //   https://www.drupal.org/node/2423913
-              $path = \Drupal::l($path, Url::fromUserInput('/' . $path));
+              $path = \Drupal::l($path->toString(), $path);
             }

So, the change for first arg to \Drupal::l() makes sense. The change for the second one has me scratching my head.

So, in HEAD, we had $path which was really a Url object (via $this->executable->getUrl() about 120 lines up), which got implicitly converted to a string via the magic method when it was prepended with a '/', and we then converted it back to a Url object using Url::fromUserInput(). Wuh?

The patch just uses the $path via $this->executable->getUrl() as is.

This seems the proper thing, but is there anything we are losing by not having it go through fromUserInput()?

dawehner’s picture

So, in HEAD, we had $path which was really a Url object (via $this->executable->getUrl() about 120 lines up), which got implicitly converted to a string via the magic method when it was prepended with a '/', and we then converted it back to a Url object using Url::fromUserInput(). Wuh?

My thoughts!

This seems the proper thing, but is there anything we are losing by not having it go through fromUserInput()?

The only thing we don't loose is that this no longer point to the wrong path. Have a look at http://d8.dev/admin/structure/views/view/content in HEAD, its pointing to http://d8.dev/content

mpdonadio’s picture

Status: Needs review » Reviewed & tested by the community

Did another look through this I think this is good to go. Working on a CR.

dawehner’s picture

Thank you!

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

This issue addresses a major bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 01382db and pushed to 8.0.x. Thanks!

  • alexpott committed 01382db on 8.0.x
    Issue #2416971 by dawehner, pwolanin: Remove Url::__toString()
    
jbrown’s picture

There are hundreds more classes with __toString(). Do we need to remove them also?

dawehner’s picture

There are hundreds more classes with __toString(). Do we need to remove them also?

Well, those not throw an exception, right?

jbrown’s picture

Sounds dangerous - any function can throw an exception.

I implemented Drupal's double exception handling years ago: http://jonathanpatrick.me/blog/drupal7-exception-handling

Seems that this is another situation where the same problem can occur.

Should we implement a trait with a safe __toString implementation that wraps a try / catch ?

Wim Leers’s picture

#44: quoting the IS:

You cannot throw exceptions in __toString() methods, because PHP will fatal
if you do that, see http://3v4l.org/Y33D8

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

AaronBauman’s picture

PHP 7.4+ adds support for exception handling in magic methods.
So for D9.3.x when 7.4 is the minimum supported PHP version, we can re-add __toString() method yeah?
Is there another issue for this open already?