Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:104540 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 6459 invoked from network); 1 Mar 2019 20:41:50 -0000 Received: from unknown (HELO mail-it1-f172.google.com) (209.85.166.172) by pb1.pair.com with SMTP; 1 Mar 2019 20:41:50 -0000 Received: by mail-it1-f172.google.com with SMTP id d125so10888243ith.1 for ; Fri, 01 Mar 2019 09:29:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=y9bBMmv9XUzGXfTVS5kUm8vNhXRl+rCOrO0zV2FBb1A=; b=cvrSBBOUtHL4ijxCKlWaSmOmXnruBob81XmeyV/clq3pZrv8XDJUF5dO/aivR+GNoo Hb/s/8yPVJcLKjLWMqoM3knOaI5nTScc+fT6q+QLJTeVb78Z4og9NdooZyi4tPWKckeG YUHoeoa8wv4bmzRrBZyXuTA4fPR+5C+g8Xcz/kRqSqXsZ3nuRrCrp5w1PdFGmc51ELDE hh/rz54YQt2VNlCONz753ilZfPTqN7tLNwnKKZCSViKZyS8z7RJWNd9YNQJi/WMWg3bM 6tL5eVxyIJE+idnbsphR2SIclDY3WLzz+sXNNJOwmWU2xFSzd5gjvNuyT4ufsGOWj29S ZXkQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=y9bBMmv9XUzGXfTVS5kUm8vNhXRl+rCOrO0zV2FBb1A=; b=L8HWnEC2K7HU69u0NdeK3/Niq8e3yV/MJGkvh822/h1eoSeK7rfZYNXapbOABPyK6L jkroTIWD7ASXchWWytUGzvmQdlGocoaDrYgYbBYmNi8xAtTeJmEeGhgtgmvlpgnlcwUn 1nc411Tq06Tnri1jAI3klmL9/h5y6sYSDTBSPoPJd2LqyVGVhOZBACWDKiWZzIqNqHIu rAM44twong+FGeg4gmMxyyhVlpBES5OmAgYxsiyG+mfMVToTCoXebD0dNH4xVvrSCu7V UpiyzV1HwbrKx4lTbM0INgtScpIPRp/OMio3faq+D0HRb4B1oc0bO7+q7iDyCNoIorMM g3Hg== X-Gm-Message-State: AHQUAuZXIf14Gf8+MnRQf2sXM6MWQk9fG1Hgjp8qqKlCJMiR7FjnoMGa wJcPjtDHn1VEfdKVwl+VMfzv9/24BnRnaxux+iA= X-Google-Smtp-Source: AHgI3Ibu96IveIGR1Zp5CGnlHAbDDHGueuPctij8O0KB45TIUhzaG3+akCDcSbtanwBXlf68q3+7Lxf/izGS0Kly14g= X-Received: by 2002:a24:1091:: with SMTP id 139mr3938446ity.110.1551461341157; Fri, 01 Mar 2019 09:29:01 -0800 (PST) MIME-Version: 1.0 References: <1551457459.2096.19.camel@schlueters.de> In-Reply-To: <1551457459.2096.19.camel@schlueters.de> Date: Fri, 1 Mar 2019 18:28:44 +0100 Message-ID: To: =?UTF-8?Q?Johannes_Schl=C3=BCter?= Cc: PHP internals Content-Type: multipart/alternative; boundary="0000000000001f60a905830bbe56" Subject: Re: [PHP-DEV] Allow throwing from __toString() From: nikita.ppv@gmail.com (Nikita Popov) --0000000000001f60a905830bbe56 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Fri, Mar 1, 2019 at 5:24 PM Johannes Schl=C3=BCter wrote: > On Fr, 2019-03-01 at 12:25 +0100, Nikita Popov wrote: > > > For extension authors, the guideline is: > > Will zend_parse_paramters and related detect if an exception is thrown > and fail? > Yes, of course :) That's one of the most important cases. I believe things like database (or other network) extensions have to be > really carefully checked, not that we store corrupted data (empty > string) in the database (or otherwise send via network) while returning > an error to the user. > > > Simple 5 minute example based on your branch: > > > class throws { > function __toString() { > throw new Exception("Sorry"); > } > } > > $db =3D new sqlite3(':memory:'); > $db->exec('CREATE TABLE t(id int, v varchar(255))'); > > $stmt =3D $db->prepare('INSERT INTO t VALUES(:i, :v)'); > $stmt->bindValue('i', 1234); > $stmt->bindValue('v', new throws); > > try { > $stmt->execute(); > } catch (Exception $e) { > echo "Exception thrown ...\n"; > } > > $stmt->execute(); > > $query =3D $db->query("SELECT * FROM t"); > while ($row =3D $query->fetchArray(SQLITE3_ASSOC)) { > print_r($row); > } > ?> > > This prints > > Exception thrown ... > Array > ( > [id] =3D> 1234 > [v] =3D> > ) > > So during the first execution it notices that the conversion went wrong > and aborts the operation, but it keeps th emtpy string as bound value. > On second execute it re-uses the values and doesn't notice the error. > Thanks for the example. I agree that keeping the empty value is not the correct behavior and have fixed this in https://github.com/php/php-src/pull/3887/commits/ac14ab02912b267dd370396937= a971ff0b4daec8. I will also review the other database binding code, because I think I made the same mistake there. So there's one more point to add to the extension guidelines: * When working on non-temporary zvals, avoid leaving behind the converted string in case of exception. Prefer zend_string *str =3D zval_get_string(val); if (EG(exception)) { return; } // Do something with str zend_string_release(str); over convert_to_string(val); if (EG(exception)) { return; } // Do something with Z_STR_P(val) Of course, this should be preferred for other reasons as well. While working on this patch I've already fixed many illegal uses of convert_to_string, which were modifying shared values in-place. > I fear we have many such cases which are subtle ad hard to find without > deep review of any string conversion. And in future we will introduce > bugs due to this in places where new conversions are added ... I'm happy to find and fix these bugs, rather than leave the language buggy by design ;) Regards, Nikita On Fri, Mar 1, 2019 at 5:24 PM Johannes Schl=C3=BCter wrote: > On Fr, 2019-03-01 at 12:25 +0100, Nikita Popov wrote: > > > For extension authors, the guideline is: > > Will zend_parse_paramters and related detect if an exception is thrown > and fail? > > I believe things like database (or other network) extensions have to be > really carefully checked, not that we store corrupted data (empty > string) in the database (or otherwise send via network) while returning > an error to the user. > > > Simple 5 minute example based on your branch: > > > class throws { > function __toString() { > throw new Exception("Sorry"); > } > } > > $db =3D new sqlite3(':memory:'); > $db->exec('CREATE TABLE t(id int, v varchar(255))'); > > $stmt =3D $db->prepare('INSERT INTO t VALUES(:i, :v)'); > $stmt->bindValue('i', 1234); > $stmt->bindValue('v', new throws); > > try { > $stmt->execute(); > } catch (Exception $e) { > echo "Exception thrown ...\n"; > } > > $stmt->execute(); > > $query =3D $db->query("SELECT * FROM t"); > while ($row =3D $query->fetchArray(SQLITE3_ASSOC)) { > print_r($row); > } > ?> > > This prints > > Exception thrown ... > Array > ( > [id] =3D> 1234 > [v] =3D> > ) > > So during the first execution it notices that the conversion went wrong > and aborts the operation, but it keeps th emtpy string as bound value. > On second execute it re-uses the values and doesn't notice the error. > > I fear we have many such cases which are subtle ad hard to find without > deep review of any string conversion. And in future we will introduce > bugs due to this in places where new conversions are added ... > > johannes > --0000000000001f60a905830bbe56--