rfc:is_literal

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
Next revisionBoth sides next revision
rfc:is_literal [2020/03/21 17:38] – created craigfrancisrfc:is_literal [2021/04/19 13:44] – Updated examples, and general tweaks craigfrancis
Line 1: Line 1:
-https://wiki.php.net/rfc/is_literal?do=edit 
- 
 ====== PHP RFC: Is Literal Check ====== ====== PHP RFC: Is Literal Check ======
  
-  * Version: 0.1+  * Version: 0.5
   * Date: 2020-03-21   * Date: 2020-03-21
 +  * Updated: 2021-04-19
   * Author: Craig Francis, craig#at#craigfrancis.co.uk   * Author: Craig Francis, craig#at#craigfrancis.co.uk
   * Status: Draft   * Status: Draft
   * First Published at: https://wiki.php.net/rfc/is_literal   * First Published at: https://wiki.php.net/rfc/is_literal
 +  * GitHub Repo: https://github.com/craigfrancis/php-is-literal-rfc
  
 ===== Introduction ===== ===== Introduction =====
  
-Add an //is_literal()// functionso developers/frameworks can be sure they are working with safe value one created from one or more literalsdefined within PHP scripts.+This RFC proposes a new function, //is_literal(string $string)//, to help enforce separation of hard-coded valuesfrom user-supplied data.
  
-This allows developers/frameworksat runtime, to warn or block SQL Injection, Command Line Injection, and many cases of HTML Injection.+This addresses some of the same use cases as "taint flags"but is both simpler and stricter. It does not address how user data is transmitted or escapedonly whether it has been passed to a particular library function separately from the programmer defined values.
  
-It allows commands to be testedto ensure they are a "programmer supplied constant/static/validated string", and all other unsafe variables are provided separately (as noted by [[https://news-web.php.net/php.internals/87725|Yasuo Ohgaki]]).+The clearest example is a database library which supports parametrised queries at the driver levelwhere the programmer could use either of these:
  
-This will also allow systems/frameworks to decide if they want to **block**, **educate** (via a notice), or **ignore** these issues (to avoid the "don't nanny" concern raised by [[https://news-web.php.net/php.internals/87383|Lester Caine]]).+<code php> 
 +$db->query('SELECT FROM users WHERE id = . $_GET['id']); // INSECURE
  
-===== Related JavaScript Implementation =====+$db->query('SELECT * FROM users WHERE id ?', [$_GET['id']]); 
 +</code>
  
-This proposal is taking some ideas from TC39, where similar idea is being discussed for JavaScriptto support the introduction of Trusted Types.+By rejecting the SQL that was not written as literal (first example), the library can provide protection against this incorrect use.
  
-https://github.com/tc39/proposal-array-is-template-object\\ +===== Examples =====
-https://github.com/mikewest/tc39-proposal-literals +
- +
-They are looking at "Distinguishing strings from a trusted developer, from strings that may be attacker controlled"+
- +
-===== Taint Checking ===== +
- +
-Xinchen Hui has done some amazing work with the Taint extension: +
- +
-https://github.com/laruence/taint +
- +
-Unfortunately this approach does not address all issues, mainly because it still allows string escaping, which is only "[[https://www.php.net/manual/en/pdo.quote.php|Theoretically Safe]]" (typically due to character encoding issues), nor does it address issues such as missing quotes: +
- +
-  $sql = 'DELETE FROM table WHERE id = ' . mysqli_real_escape_string($db, $_GET['id']); +
-   +
-  // delete.php?id=id +
-   +
-  // DELETE FROM table WHERE id = id+
  
-  $html = '<img src=' . htmlentities($_GET['url']) />'; +The [[https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/query-builder.html#high-level-api-methods|Doctrine Query Builder]] allows custom WHERE clauses to be provided as strings. This is intended for use with literals and placeholders, but does not protect against this simple mistake:
-   +
-  // example.php?url=x%20onerror=alert(cookie) +
-   +
-  // <img src=x onerror=alert(cookie) />+
  
-The Taint extension also [[https://github.com/laruence/taint/blob/4a6c4cb2613e27f5604d2021802c144a954caff8/taint.c#L63|conflicts with XDebug]] (sorry Derick),+<code php> 
 +// INSECURE 
 +$qb->select('u'
 +   ->from('User', 'u'
 +   ->where('u.id = ' $_GET['id']) 
 +</code>
  
-===== Previous RFC =====+The definition of the //where()// method could check with //is_literal()// and throw an exception advising the programmer to replace it with a safer use of placeholders:
  
-Matt Tait suggested [[https://wiki.php.net/rfc/sql_injection_protection||Automatic SQL Injection Protection]].+<code php
 +$qb->select('u'
 +   ->from('User', 'u'
 +   ->where('u.id = :identifier'
 +   ->setParameter('identifier', $_GET['id']); 
 +</code>
  
-It was noted that "unfiltered input can affect way more than only SQL" ([[https://news-web.php.net/php.internals/87355|Pierre Joye]])and this amount of work isn't ideal for "just for one use case" ([[https://news-web.php.net/php.internals/87647|Julien Pauli]]).+Similarly, Twig allows [[https://twig.symfony.com/doc/2.x/recipes.html#loading-a-template-from-a-string|loading a template from a string]], which could allow accidentally skipping the default escaping functionality:
  
-Where it would have effected every SQL function, such as //mysqli_query()//, //$pdo->query()//, //odbc_exec()//, etc (concerns raised by [[https://news-web.php.net/php.internals/87436|Lester Caine]] and [[https://news-web.php.net/php.internals/87650|Anthony Ferrara]]).+<code php> 
 +// INSECURE 
 +echo $twig->createTemplate('<p>Hi ' . $_GET['name''</p>')->render(); 
 +</code>
  
-And each of those functions would need a bypass for cases where unsafe SQL was intentionally being used (e.g. phpMyAdmin taking SQL from POST databecause some applications intentionally "pass raw, user submitted, SQL" (Ronald Chmara [[https://news-web.php.net/php.internals/87406|1]]/[[https://news-web.php.net/php.internals/87446|2]]).+If //createTemplate()// checked with //is_literal()//, the programmer could be advised to write this instead:
  
-I also agree that "SQL injection is almost a solved problem [by using] prepared statements" ([[https://news-web.php.net/php.internals/87400|Scott Arciszewski]]), but we do need something that can identify mistakes, ideally at runtime.+<code php> 
 +echo $twig->createTemplate('<p>Hi {{ name }}</p>')->render(['name' => $_GET['name']])
 +</code>
  
 ===== Proposal ===== ===== Proposal =====
  
-Add an //is_literal()// function to check if a given variable has only been created by Literal(s).+A literal is defined as a value (stringwhich has been written by the programmer. The value may be passed between functions, as long as it is not modified in any way other than string concatenation.
  
-This uses a similar definition as the [[https://wiki.php.net/rfc/sql_injection_protection#safeconst|SafeConst]] by Matt Tait, but it does not need to accept Integer or FloatingPoint variables as safe (unless it makes the implementation easier), nor should it effect any existing functions.+<code php
 +is_literal('Example'); // true
  
-Thanks to [[https://news-web.php.net/php.internals/87396|Xinchen Hui]], we know the PHP5 Taint extension was complex, but "with PHP7's new zend_string, and string flags, the implementation will become easier".+$a = 'Example'; 
 +is_literal($a); // true
  
-Unlike the Taint extension, there is no need to provide an equivalent //untaint()// function.+is_literal(4); // true 
 +is_literal(0.3)// true 
 +is_literal('a' 'b'); // true, compiler can concatenate
  
-===== Examples =====+$a 'A'; 
 +$b $a . ' B ' . 3; 
 +is_literal($b); // true, ideally (more details below)
  
-==== SQL Injection, Basic ====+is_literal($_GET['id']); // false
  
-A simple example:+is_literal(rand(0, 10)); // false
  
-  $sql = 'SELECT * FROM table WHERE id = ?'+is_literal(sprintf('LIMIT %d', 3)); // false, should use parameters 
-   +</code>
-  $result = $db->exec($sql[$id]);+
  
-Checked in the framework by:+Note that there is no way to manually mark a string as a literal (i.e. no equivalent to //untaint()//); as soon as the value has been manipulated in any way, it is no longer marked as a literal.
  
-  class db { +See the [[https://github.com/craigfrancis/php-is-literal-rfc/blob/main/justification.md|justification page]] as to why it's done this way.
-   +
-    public function exec($sql, $parameters = []) { +
-   +
-      if (!is_literal($sql)) { +
-        throw new Exception('SQL must be a literal.'); +
-      } +
-   +
-      $statement = $this->pdo->prepare($sql); +
-      $statement->execute($parameters); +
-      return $statement->fetchAll(); +
-   +
-    } +
-   +
-  }+
  
-It will also work with string concatenation:+===== Comparison to Taint Tracking =====
  
-  define('TABLE', 'example'); +Some languages implement a "taint flag" which tracks whether values are considered "safe"There is a [[https://github.com/laruence/taint|Taint extension for PHP]] by Xinchen Huiand [[https://wiki.php.net/rfc/taint|a previous RFC proposing it be added to the language]].
-   +
-  $sql = 'SELECT * FROM ' TABLE . ' WHERE id = ?'; +
-   +
-    is_literal($sql); // Returns true +
-   +
-  $sql .= ' AND id = ' . mysqli_real_escape_string($db$_GET['id']); +
-   +
-    is_literal($sql); // Returns false+
  
-==== SQL InjectionORDER BY ====+These solutions rely on the assumption that the output of an escaping function is safe for a particular context. This sounds reasonable in theorybut the operation of escaping functions, and the context for which their output is safe, are very hard to define. This leads to a feature that is both complex and unreliable.
  
-To ensure //ORDER BY// can be set via the user, but only use acceptable values:+This proposal avoids the complexity by addressing a different part of the problemseparating inputs supplied by the programmer, from inputs supplied by the user.
  
-  $order_fields +===== Previous Work =====
-      'name', +
-      'created', +
-      'admin', +
-    ]; +
-   +
-  $order_id array_search(($_GET['sort'] ?? NULL), $order_fields); +
-   +
-  $sql ' ORDER BY ' . $order_fields[$order_id];+
  
-==== SQL InjectionWHERE IN ====+Google uses a [[https://github.com/craigfrancis/php-is-literal-rfc/blob/main/justification.md#go-implementation|similar approach in Go]] to identify "compile time constants"[[https://github.com/craigfrancis/php-is-literal-rfc/blob/main/justification.md#perl-implementation|Perl has a Taint Mode]] (but uses regular expressions to un-taint data), and there are discussions about [[https://github.com/craigfrancis/php-is-literal-rfc/blob/main/justification.md#javascript-implementation|adding it to JavaScript]] to support Trusted Types.
  
-Most SQL strings can be a concatenations of literal valuesbut //WHERE x IN (?,?,?)// need to use a variable number of literal placeholders.+As noted by [[https://news-web.php.net/php.internals/109192|Tyson Andre]], it might be possible to use static analysisfor example [[https://psalm.dev/|psalm]]. But I can't find any which do these checks by default[[https://github.com/vimeo/psalm/commit/2122e4a1756dac68a83ec3f5abfbc60331630781|can be incomplete]], they are likely to miss things (especially at runtime), and we can't expect all programmers to use static analysis (especially those who are new to programming, who need this more than developers who know the concepts and just make the odd mistake).
  
-So there //might// need to be special case for //array_fill()//+//implode()// or //str_repeat()//+//substr()// to create something like '?,?,?'+And there is the [[https://wiki.php.net/rfc/sql_injection_protection|Automatic SQL Injection Protection]] RFC by Matt Tait, where this RFC uses similar concept of the [[https://wiki.php.net/rfc/sql_injection_protection#safeconst|SafeConst]]. When Matt's RFC was being discussedit was noted:
  
-  $in_sql = implode(',', array_fill(0, count($ids), '?')); +  * "unfiltered input can affect way more than only SQL" ([[https://news-web.php.net/php.internals/87355|Pierre Joye]]); 
-   +  * this amount of work isn't ideal for "just for one use case" ([[https://news-web.php.net/php.internals/87647|Julien Pauli]]); 
-  // or +  * It would have effected every SQL function, such as //mysqli_query()//, //$pdo->query()//, //odbc_exec()//etc (concerns raised by [[https://news-web.php.net/php.internals/87436|Lester Caine]] and [[https://news-web.php.net/php.internals/87650|Anthony Ferrara]])
-   +  * Each of those functions would need a bypass for cases where unsafe SQL was intentionally being used (e.g. phpMyAdmin taking SQL from POST databecause some applications intentionally "pass rawuser submittedSQL" (Ronald Chmara [[https://news-web.php.net/php.internals/87406|1]]/[[https://news-web.php.net/php.internals/87446|2]]).
-  $in_sql = substr(str_repeat('?,', count($ids)), 0, -1);+
  
-To be used with:+I also agree that "SQL injection is almost a solved problem [by using] prepared statements" ([[https://news-web.php.net/php.internals/87400|Scott Arciszewski]]), and this is where //is_literal()// can be used to check that no mistakes are made.
  
-  $sql 'SELECT * FROM table WHERE id IN (' . $in_sql . ')';+===== Usage =====
  
-==== SQL Injection, ORM Usage ====+By libraries:
  
-[[https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/query-builder.html#high-level-api-methods|Doctrine]] could use this to ensure //$predicates// is a literal: +<code php
- +function literal_check($var{ 
-  $users = $queryBuilder +  if (function_exists('is_literal') && !is_literal($var)) { 
-    ->select('u') +    $level 2; // Get from config, defaults to 1
-    ->from('User', 'u'+    if ($level === 0{ 
-    ->where('u.id = ' . $_GET['id']) +      // Programmer aware, and is choosing to bypass this check. 
-    ->getQuery() +    } else if ($level === 1) { 
-    ->getResult(); +      trigger_error('Non-literal detected!', E_USER_NOTICE); 
-   +    } else 
-  // example.php?id=u.id +      throw new Exception('Non-literal detected!');
- +
-Where this mistake could be identified by: +
- +
-  public function where($predicates+
-  +
-      if (!is_literal($predicates)) { +
-          throw new Exception('Can only accept a literal'); +
-      } +
-      ... +
-  } +
- +
-[[https://redbeanphp.com/index.php?p=/finding|RedBean]] could check //$sql// is a literal: +
- +
-  $users R::find('user', 'id = ' . $_GET['id']); +
- +
-[[http://propelorm.org/Propel/reference/model-criteria.html#relational-api|PropelORM]] could check //$clause// is a literal: +
- +
-  $users = UserQuery::create()->where('id = ' . $_GET['id'])->find(); +
- +
-==== SQL Injection, ORM Internal ==== +
- +
-The //is_literal()// function could be used by ORM developers, so they can be sure they have created an SQL string out of literals. +
- +
-This would avoid mistakes such as the ORDER BY issues in the Zend framework [[https://framework.zend.com/security/advisory/ZF2014-04|1]]/[[https://framework.zend.com/security/advisory/ZF2016-03|2]]. +
- +
-==== CLI Injection ==== +
- +
-Rather than using functions such as: +
- +
-  * //exec()// +
-  * //shell_exec()// +
-  * //system()// +
-  * //passthru()// +
- +
-Frameworks (or PHP) could introduce something similar to //pcntl_exec()//, where arguments are provided separately. +
- +
-Or, take a verified literal for the command, and use parameters for the arguments (like SQL): +
- +
-  $output = parameterised_exec('grep ? /path/to/file | wc -l', [ +
-      'example', +
-    ]); +
- +
-Rough implementation: +
- +
-  function parameterised_exec($cmd, $args = []) { +
-   +
-    if (!is_literal($cmd)) { +
-      throw new Exception('The first argument must be a literal'); +
-    } +
-   +
-    $offset 0; +
-    $k 0; +
-    while (($pos strpos($cmd, '?', $offset)) !== false) { +
-      if (!isset($args[$k])) { +
-        throw new Exception('Missing parameter "' . ($k + 1) . '"'); +
-        exit(); +
-      } +
-      $arg = escapeshellarg($args[$k]); +
-      $cmd = substr($cmd0, $pos. $arg . substr($cmd, ($pos + 1)); +
-      $offset = ($pos + strlen($arg)); +
-      $k+++
-    } +
-    if (isset($args[$k])) +
-      throw new Exception('Unused parameter ". ($k + 1) . '"'); +
-      exit();+
     }     }
-   
-    return exec($cmd); 
-   
   }   }
 +}
  
-==== HTML Injection ====+function example($input) { 
 +  literal_check($input); 
 +  // ... 
 +}
  
-Template engines should receive variables separately from the raw HTML.+example('hello'); // OK 
 +example(strtoupper('hello')); // Exception thrown: the result of strtoupper is a new, non-literal string 
 +</code>
  
-Often the engine will get the HTML from static files:+Table and Fields in SQL, which cannot use parameters; for example //ORDER BY//:
  
-  $html file_get_contents('/path/to/template.html');+<code php> 
 +$order_fields 
 +    'name'
 +    'created', 
 +    'admin', 
 +  ];
  
-But small snippets of HTML are often easier to define as a literal within the PHP script:+$order_id = array_search(($_GET['sort'] ?? NULL), $order_fields);
  
-  $template_html = ' +$sql = ' ORDER BY ' . $order_fields[$order_id]; 
-    <p>Hello <span id="username"></span></p> +</code>
-    <p><a>Website</a></p>';+
  
-Where the variables are supplied separately, in this example I'm using XPaths:+Undefined number of parameters; for example //WHERE IN//:
  
-  $values = [ +<code php
-      '//span[@id="username"]' =[ +function where_in_sql($count) { // Should check for 0 
-          NULL      => 'Name', // The textContent +  $sql = '?'; 
-          'class'   => 'admin', +  for ($1; $k < $count; $k++) { 
-          'data-id' => '123', +    $sql .= ',?';
-        ], +
-      '//a' => [ +
-          'href' => 'https://example.com', +
-        ], +
-    ]; +
-   +
-  echo template_parse($template_html, $values); +
- +
-Being sure the HTML does not contain unsafe variables, the templating engine can accept and apply the supplied variables for the relevant context, for example: +
- +
-  function template_parse($html, $values) { +
-   +
-    if (!is_literal($html)) { +
-      throw new Exception('Invalid Template HTML.'); +
-    } +
-   +
-    $dom new DomDocument(); +
-    $dom->loadHTML('<?xml encoding="UTF-8">. $html)+
-   +
-    $xpath = new DOMXPath($dom); +
-   +
-    foreach ($values as $query => $attributes) { +
-   +
-      if (!is_literal($query)) { +
-        throw new Exception('Invalid Template XPath.'); +
-      } +
-   +
-      foreach ($xpath->query($query) as $element) { +
-        foreach ($attributes as $attribute => $value) { +
-   +
-          if (!is_literal($attribute)) { +
-            throw new Exception('Invalid Template Attribute.'); +
-          } +
-   +
-          if ($attribute) { +
-            $safe = false; +
-            if ($attribute == 'href') { +
-              if (preg_match('/^https?:\/\//', $value)) { +
-                $safe = true; // Not "javascript:..." +
-              } +
-            } else if ($attribute == 'class') { +
-              if (in_array($value['admin', 'important'])) { +
-                $safe = true; // Only allow specific classes? +
-              } +
-            } else if (preg_match('/^data-[a-z]+$/', $attribute)) { +
-              if (preg_match('/^[a-z0-9 ]+$/i', $value)) { +
-                $safe = true; +
-              } +
-            } +
-            if ($safe) { +
-              $element->setAttribute($attribute, $value); +
-            } +
-          } else { +
-            $element->textContent = $value; +
-          } +
-   +
-        } +
-      } +
-   +
-    } +
-   +
-    $html = ''; +
-    $body = $dom->documentElement->firstChild; +
-    if ($body->hasChildNodes()) { +
-      foreach ($body->childNodes as $node) { +
-        $html .= $dom->saveXML($node); +
-      } +
-    } +
-   +
-    return $html; +
-  +
   }   }
 +  return $sql;
 +}
 +$sql = 'WHERE id IN (' . where_in_sql(count($ids)) . ')';
 +</code>
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
  
-Not sure+None
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
  
-PHP 8?+PHP 8.1?
  
 ===== RFC Impact ===== ===== RFC Impact =====
Line 343: Line 188:
 ===== Open Issues ===== ===== Open Issues =====
  
-  Can //array_fill()//+//implode()// or //str_repeat()//+//substr()// pass though the "is_literal" flag for the "WHERE IN" case? +On [[https://github.com/craigfrancis/php-is-literal-rfc/issues|GitHub]]: 
-  - Systems/Frameworks that define certain variables (e.g. table name prefixes) without the use of a literal (e.g. ini/json/yaml files), won't be able to use this check, as originally noted by [[https://news-web.php.net/php.internals/87667|Dennis Birkholz]].+ 
 +  - Name it something else? [[https://news-web.php.net/php.internals/109197|Jakob Givoni]] suggested //is_from_literal()//
 +  - Would this cause performance issues? A [[https://github.com/craigfrancis/php-is-literal-rfc/blob/main/tests/001.phpt|basic string concat test]], just focusing on string concat (worst case scenario), shows a 1.3% increase in processing time (1.341s to 1.358s = +0.017s). 
 +  - Systems/Frameworks that define certain variables (e.g. table name prefixes) without the use of a literal (e.g. ini/json/yaml files), they might need to make some changes to use this check, as originally noted by [[https://news-web.php.net/php.internals/87667|Dennis Birkholz]].
  
 ===== Unaffected PHP Functionality ===== ===== Unaffected PHP Functionality =====
Line 352: Line 200:
 ===== Future Scope ===== ===== Future Scope =====
  
-Certain functions (mysqli_query, preg_match, etc) might use this information to generate error/warning/notice.+As noted by [[https://chat.stackoverflow.com/transcript/message/51573226#51573226|MarkR]], the biggest benefit will come when it can be used by PDO and similar functions (//mysqli_query////preg_match//, //exec//, etc). But the basic idea can be used immediately by frameworks and general abstraction libraries, and they can give feedback for future work. 
 + 
 +**Phase 2** could introduce a way for programmers to specify that certain function arguments only accept safe literals, and/or specific value-objects their project trusts (this idea comes from [[https://web.dev/trusted-types/|Trusted Types]] in JavaScript). 
 + 
 +For example, project could require the second argument for //pg_query()// only accept literals or their //query_builder// object (which provides a //__toString// method); and that any output (print, echo, readfile, etc) must use the //html_output// object that's returned by their trusted HTML Templating system (using //ob_start()// might be useful here). 
 + 
 +**Phase 3** could set a default of 'only literals' for all of the relevant PHP function arguments, so developers are given a warning, and later prevented (via an exception), when they provide an unsafe value to those functions (they could still specify that unsafe values are allowed, e.g. phpMyAdmin). 
 + 
 +And, for a bit of silliness (Spaß ist verboten), there could be a //is_figurative()// function, which MarkR seems to [[https://chat.stackoverflow.com/transcript/message/48927770#48927770|really]], [[https://chat.stackoverflow.com/transcript/message/51573091#51573091|want]] :-)
  
 ===== Proposed Voting Choices ===== ===== Proposed Voting Choices =====
  
-Not sure+N/A
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
  
-volunteer is needed to help with implementation.+N/A
  
 ===== Implementation ===== ===== Implementation =====
  
-N/A+Joe Watkins has [[https://github.com/php/php-src/compare/master...krakjoe:literals|created an implementation]] which includes string concat. While the performance impact needs to be considered, this would provide the easiest solution for projects already using string concat for their parameterised SQL. 
 + 
 +Dan Ackroyd also [[https://github.com/php/php-src/compare/master...Danack:is_literal_attempt_two|started an implementation]], which uses functions like [[https://github.com/php/php-src/compare/master...Danack:is_literal_attempt_two#diff-2b0486443df74cd919c949f33f895eacf97c34b8490e7554e032e770ab11e4d8R2761|literal_combine()]] to avoid performance concerns.
  
 ===== References ===== ===== References =====
  
-- https://wiki.php.net/rfc/sql_injection_protection+N/A
  
 ===== Rejected Features ===== ===== Rejected Features =====
  
 N/A N/A
 +
 +===== Thanks =====
 +
 +  - **Dan Ackroyd**, DanAck, for surprising me with the first implementation, and getting the whole thing started.
 +  - **Joe Watkins**, krakjoe, for finding how to set the literal flag, and creating the implementation that supports string concat.
 +  - **Rowan Tommins**, IMSoP, for re-writing this RFC to focus on the key features, and putting it in context of how it can be used by libraries.
 +  - **Nikita Popov**, NikiC, for suggesting where the literal flag could be stored. Initially this was going to be the [[https://chat.stackoverflow.com/transcript/message/51565346#51565346|GC_PROTECTED flag for strings]], which allowed Dan to start the first implementation.
 +  - **MarkR, **for alternative ideas, and noting that "interned strings in PHP have a flag" [[https://chat.stackoverflow.com/transcript/message/48927813#48927813|source]], which started the conversation on how this could be implemented.
 +  - **Xinchen Hui**, who created the Taint Extension, allowing me to test the idea; and noting how Taint in PHP5 was complex, but "with PHP7's new zend_string, and string flags, the implementation will become easier" [[https://news-web.php.net/php.internals/87396|source]].
  
rfc/is_literal.txt · Last modified: 2022/02/14 00:36 by craigfrancis