Repository navigation
fix!: Fix invalid syntax and unexpected behavior in PHP generator #10661
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,10 +25,10 @@ import type {Workspace} from '../../core/workspace.js'; | |
| // prettier-ignore | ||
| export enum Order { | ||
| ATOMIC = 0, // 0 "" ... | ||
| CLONE = 1, // clone | ||
| NEW = 1, // new | ||
| MEMBER = 2.1, // [] | ||
| FUNCTION_CALL = 2.2, // () | ||
| MEMBER = 1.1, // [] | ||
| FUNCTION_CALL = 1.2, // () | ||
| CLONE = 2, // clone | ||
| NEW = 2, // new | ||
| POWER = 3, // ** | ||
| INCREMENT = 4, // ++ | ||
| DECREMENT = 4, // -- | ||
|
|
@@ -66,6 +66,10 @@ export enum Order { | |
| export class PhpGenerator extends CodeGenerator { | ||
| /** List of outer-inner pairings that do NOT require parentheses. */ | ||
| ORDER_OVERRIDES: [Order, Order][] = [ | ||
| // (foo())[0] -> foo()[0] | ||
| [Order.FUNCTION_CALL, Order.MEMBER], | ||
| // (foo())() -> foo()() | ||
| [Order.FUNCTION_CALL, Order.FUNCTION_CALL], | ||
| // (foo()).bar() -> foo().bar() | ||
| // (foo())[0] -> foo()[0] | ||
| [Order.MEMBER, Order.FUNCTION_CALL], | ||
|
|
@@ -84,6 +88,9 @@ export class PhpGenerator extends CodeGenerator { | |
| [Order.LOGICAL_OR, Order.LOGICAL_OR], | ||
| ]; | ||
|
|
||
| /** Whether to emit the opening <?php tag when generating code. */ | ||
| protected outputOpeningTag = true; | ||
|
|
||
| /** @param name Name of the language the generator is for. */ | ||
| constructor(name = 'PHP') { | ||
| super(name); | ||
|
|
@@ -173,7 +180,12 @@ export class PhpGenerator extends CodeGenerator { | |
| this.isInitialized = false; | ||
|
|
||
| this.nameDB_!.reset(); | ||
| return definitions.join('\n\n') + '\n\n\n' + code; | ||
| return ( | ||
| (this.outputOpeningTag ? '<?php\n\n' : '') + | ||
| definitions.join('\n\n') + | ||
| '\n\n\n' + | ||
| code | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -312,9 +324,80 @@ export class PhpGenerator extends CodeGenerator { | |
| if (negate) { | ||
| at = delta ? `-(${at})` : `-${at}`; | ||
| } | ||
| if (Math.floor(order) >= Math.floor(orderForInput)) { | ||
| if (Math.floor(order) <= Math.floor(orderForInput)) { | ||
| at = `(${at})`; | ||
| } | ||
| return at; | ||
| } | ||
|
|
||
| /** | ||
| * Sets whether or not this generator outputs the opening <?php tag when | ||
| * generating code. Defaults to true. | ||
| * | ||
| * @param outputTag True to include the opening PHP tag in generated code, or | ||
| * false to omit it. | ||
| */ | ||
| setOutputOpeningTag(outputTag: boolean) { | ||
| this.outputOpeningTag = outputTag; | ||
| } | ||
|
|
||
| /** | ||
| * Defines a class used by Blockly's built-in list blocks. PHP passes by | ||
| * value, including `array()`; using an object-based array wrapper gets us | ||
| * behavior that is more or less consistent with pass by reference semantics, | ||
| * consistent with the other languages we provide generators for. This is | ||
| * particularly important for variables that contain lists, where one would | ||
| * quite reasonably assume that mutating a list variable actually does so. See | ||
| * #1201 for more context. | ||
| * @internal | ||
| */ | ||
| provideListObject() { | ||
| // This isn't technically a function, but this gets us one-time-injection of | ||
| // a class definition just as well. | ||
| this.provideFunction_( | ||
| 'ListObject', | ||
| ` | ||
| class ListObject extends ArrayObject { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Would it be worth giving it a more specific name, such as Additionally, maybe we should only declare the class if it doesn't exist, in case the code is run alongside some other generated PHP code, e.g.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In general we more or less do have to assume that the generated code is run in isolation, because user-defined variable or procedure names, while kept unique within the generated code, have the same issue with conflicts if they were dropped into the middle of an external codebase. I did consider using something like |
||
| public function slice($offset, $length = null) { | ||
| $values = $this->getArrayCopy(); | ||
| $result = array_slice($values, $offset, $length); | ||
| return new ListObject($result); | ||
| } | ||
|
|
||
| public function splice($offset, $length = null, $replacement = []) { | ||
| $values = $this->getArrayCopy(); | ||
| $result = array_splice($values, $offset, $length, $replacement); | ||
| $this->exchangeArray($values); | ||
| return new ListObject($result); | ||
| } | ||
|
|
||
| public function shift() { | ||
| $values = $this->getArrayCopy(); | ||
| $result = array_shift($values); | ||
| $this->exchangeArray($values); | ||
| return $result; | ||
| } | ||
|
|
||
| public function unshift(...$newValues) { | ||
| $values = $this->getArrayCopy(); | ||
| $result = array_unshift($values, ...$newValues); | ||
| $this->exchangeArray($values); | ||
| return $result; | ||
| } | ||
|
|
||
| public function pop() { | ||
| $values = $this->getArrayCopy(); | ||
| $result = array_pop($values); | ||
| $this->exchangeArray($values); | ||
| return $result; | ||
| } | ||
|
|
||
| public function __toString() | ||
| { | ||
| return implode(', ', $this->getArrayCopy()); | ||
| } | ||
| } | ||
| `, | ||
| ); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.