33namespace JsPhpize ;
44
55use Exception ;
6- use JsPhpize \Compiler \Exception as CompilerException ;
7- use JsPhpize \Lexer \Exception as LexerException ;
8- use JsPhpize \Parser \Exception as ParserException ;
6+ use JsPhpize \Traits \Compilation ;
97use Phug \AbstractCompilerModule ;
108use Phug \Compiler ;
9+ use Phug \Compiler \Event \NodeEvent ;
1110use Phug \CompilerEvent ;
12- use Phug \CompilerInterface ;
11+ use Phug \Formatter \Element \KeywordElement ;
12+ use Phug \Parser \Node \CommentNode ;
13+ use Phug \Parser \Node \KeywordNode ;
14+ use Phug \Parser \Node \TextNode ;
1315use Phug \Renderer ;
1416use Phug \Util \ModuleContainerInterface ;
1517
1618class JsPhpizePhug extends AbstractCompilerModule
1719{
20+ use Compilation;
21+
22+ protected $ languages = ['js ' , 'php ' ];
23+
1824 public function __construct (ModuleContainerInterface $ container )
1925 {
2026 parent ::__construct ($ container );
@@ -35,6 +41,7 @@ public function __construct(ModuleContainerInterface $container)
3541
3642 //Set default options
3743 $ this ->setOptionsRecursive ([
44+ 'language ' => 'js ' ,
3845 'allowTruncatedParentheses ' => true ,
3946 'catchDependencies ' => true ,
4047 'ignoreDollarVariable ' => true ,
@@ -46,111 +53,122 @@ public function __construct(ModuleContainerInterface $container)
4653 //Apply options from container
4754 $ this ->setOptionsRecursive ($ compiler ->getOption (['module_options ' , 'jsphpize ' ]));
4855
56+ $ compiler ->attach (CompilerEvent::NODE , [$ this , 'handleNodeEvent ' ]);
57+
4958 $ compiler ->setOptionsRecursive ([
59+ 'keywords ' => [
60+ 'language ' => [$ this , 'handleLanguageKeyword ' ],
61+ 'node-language ' => [$ this , 'handleNodeLanguageKeyword ' ],
62+ ],
5063 'patterns ' => [
51- 'transform_expression ' => function ($ jsCode ) use ($ compiler ) {
52- $ jsPhpize = $ this ->getJsPhpizeEngine ($ compiler );
53- $ compilation = $ this ->compile ($ jsPhpize , $ jsCode , $ compiler ->getPath ());
54-
55- if (!($ compilation instanceof Exception)) {
56- return $ compilation ;
57- }
58-
59- return $ jsCode ;
64+ 'transform_expression ' => function ($ code ) use ($ compiler ) {
65+ return $ this ->transformExpression ($ this ->getJsPhpizeEngine ($ compiler ), $ code , $ compiler ->getPath ());
6066 },
6167 ],
6268 'checked_variable_exceptions ' => [
63- 'js-phpize ' => function ($ variable , $ index , $ tokens ) {
64- return $ index > 2 &&
65- $ tokens [$ index - 1 ] === '( ' &&
66- $ tokens [$ index - 2 ] === '] ' &&
67- is_array ($ tokens [$ index - 3 ]) &&
68- $ tokens [$ index - 3 ][0 ] === T_CONSTANT_ENCAPSED_STRING &&
69- preg_match ('/_with_ref \'$/ ' , $ tokens [$ index - 3 ][1 ]);
70- },
69+ 'js-phpize ' => [static ::class, 'checkedVariableExceptions ' ],
7170 ],
7271 ]);
7372 }
7473
75- /**
76- * @return JsPhpize
77- */
78- public function getJsPhpizeEngine (CompilerInterface $ compiler )
74+ public function handleNodeEvent (NodeEvent $ event )
7975 {
80- if (!$ compiler ->hasOption ('jsphpize_engine ' )) {
81- $ compiler ->setOption ('jsphpize_engine ' , new JsPhpize ($ this ->getOptions ()));
76+ /* @var CommentNode $node */
77+ if (($ node = $ event ->getNode ()) instanceof CommentNode &&
78+ !$ node ->isVisible () &&
79+ $ node ->hasChildAt (0 ) &&
80+ ($ firstChild = $ node ->getChildAt (0 )) instanceof TextNode &&
81+ preg_match (
82+ '/^@((?:node-)?lang(?:uage)?)([\s(].*)$/ ' ,
83+ trim ($ firstChild ->getValue ()),
84+ $ match
85+ )
86+ ) {
87+ $ keyword = new KeywordNode (
88+ $ node ->getToken (),
89+ $ node ->getSourceLocation (),
90+ $ node ->getLevel (),
91+ $ node ->getParent ()
92+ );
93+ $ keyword ->setName ($ match [1 ]);
94+ $ keyword ->setValue ($ match [2 ]);
95+ $ event ->setNode ($ keyword );
8296 }
97+ }
98+
99+ protected function getLanguageKeywordValue ($ value , KeywordElement $ keyword , $ name )
100+ {
101+ $ value = trim ($ value , "() \"' \t\n\r\0\x0B" );
102+
103+ if (!in_array ($ value , $ this ->languages )) {
104+ $ file = 'unknown ' ;
105+ $ line = 'unknown ' ;
106+ $ offset = 'unknown ' ;
107+ $ node = $ keyword ->getOriginNode ();
108+ if ($ node && ($ location = $ node ->getSourceLocation ())) {
109+ $ file = $ location ->getPath ();
110+ $ line = $ location ->getLine ();
111+ $ offset = $ location ->getOffset ();
112+ }
83113
84- return $ compiler ->getOption ('jsphpize_engine ' );
114+ throw new \InvalidArgumentException (sprintf (
115+ "Invalid argument for %s keyword: %s. Possible values are: %s \nFile: %s \nLine: %s \nOffset:%s " ,
116+ $ name ,
117+ $ value ,
118+ implode (', ' , $ this ->languages ),
119+ $ file ,
120+ $ line ,
121+ $ offset
122+ ));
123+ }
124+
125+ return $ value ;
85126 }
86127
87- /**
88- * @param JsPhpize $jsPhpize
89- * @param int $code
90- * @param string $fileName
91- *
92- * @throws Exception
93- *
94- * @return Exception|string
95- */
96- public function compile (JsPhpize $ jsPhpize , $ code , $ fileName )
128+ public function handleNodeLanguageKeyword ($ value , KeywordElement $ keyword , $ name )
97129 {
98- try {
99- $ phpCode = trim ($ jsPhpize ->compile ($ code , $ fileName ?: 'raw string ' ));
100- $ phpCode = preg_replace ('/\{\s*\}$/ ' , '' , $ phpCode );
101- $ phpCode = preg_replace (
102- '/^(?<!\$)\$+(\$[a-zA-Z \\\\\\x7f- \\xff][a-zA-Z0-9 \\\\_ \\x7f- \\xff]*\s*[=;])/ ' ,
103- '$1 ' ,
104- $ phpCode
105- );
130+ $ value = $ this ->getLanguageKeywordValue ($ value , $ keyword , $ name );
106131
107- return rtrim (trim ($ phpCode ), '; ' );
108- } catch (Exception $ exception ) {
109- if (
110- $ exception instanceof LexerException ||
111- $ exception instanceof ParserException ||
112- $ exception instanceof CompilerException
113- ) {
114- return $ exception ;
115- }
132+ if ($ next = $ keyword ->getNextSibling ()) {
133+ $ next ->prependChild (new KeywordElement ('language ' , $ value , $ keyword ->getOriginNode ()));
134+ $ next ->appendChild (new KeywordElement ('language ' , $ this ->getOption ('language ' ), $ keyword ->getOriginNode ()));
135+ }
136+
137+ return '' ;
138+ }
139+
140+ public function handleLanguageKeyword ($ value , KeywordElement $ keyword , $ name )
141+ {
142+ $ value = $ this ->getLanguageKeywordValue ($ value , $ keyword , $ name );
143+
144+ $ this ->setOption ('language ' , $ value );
116145
117- throw $ exception ;
146+ return '' ;
147+ }
148+
149+ protected function transformExpression (JsPhpize $ jsPhpize , $ code , $ fileName )
150+ {
151+ if ($ this ->getOption ('language ' ) === 'php ' ) {
152+ return $ code ;
118153 }
154+
155+ $ compilation = $ this ->compile ($ jsPhpize , $ code , $ fileName );
156+
157+ if (!($ compilation instanceof Exception)) {
158+ return $ compilation ;
159+ }
160+
161+ return $ code ;
119162 }
120163
121- /**
122- * @return array
123- */
124- public function getEventListeners ()
164+ public static function checkedVariableExceptions ($ variable , $ index , $ tokens )
125165 {
126- return [
127- CompilerEvent::OUTPUT => function (Compiler \Event \OutputEvent $ event ) {
128- /** @var CompilerInterface $compiler */
129- $ compiler = $ event ->getTarget ();
130- $ jsPhpize = $ this ->getJsPhpizeEngine ($ compiler );
131- $ output = preg_replace (
132- '/\{\s*\?><\?(?:php)?\s*\}/ ' ,
133- '{} ' ,
134- $ event ->getOutput ()
135- );
136- $ output = preg_replace (
137- '/\}\s*\?><\?(?:php)?\s*( ' .
138- 'else(if)?|for|while|switch|function ' .
139- ')(?![a-zA-Z0-9_])/ ' ,
140- '} $1 ' ,
141- $ output
142- );
143-
144- $ dependencies = $ jsPhpize ->compileDependencies ();
145- if ($ dependencies !== '' ) {
146- $ output = $ compiler ->getFormatter ()->handleCode ($ dependencies ) . $ output ;
147- }
148-
149- $ event ->setOutput ($ output );
150-
151- $ jsPhpize ->flushDependencies ();
152- $ compiler ->unsetOption ('jsphpize_engine ' );
153- },
154- ];
166+ return $ index > 2 &&
167+ $ tokens [$ index - 1 ] === '( ' &&
168+ $ tokens [$ index - 2 ] === '] ' &&
169+ !preg_match ('/^__?pug_/ ' , $ variable ) &&
170+ is_array ($ tokens [$ index - 3 ]) &&
171+ $ tokens [$ index - 3 ][0 ] === T_CONSTANT_ENCAPSED_STRING &&
172+ preg_match ('/_with_ref \'$/ ' , $ tokens [$ index - 3 ][1 ]);
155173 }
156174}
0 commit comments