├── .gitignore
├── LICENSE
├── README.md
├── bin
├── phpstormWatcher.php
└── scriptformatter.php
├── composer.json
├── composer.lock
├── lib
├── bootstrap.php
└── phptojs
│ ├── Autoloader.php
│ ├── JsPrinter
│ ├── JsPrinter.php
│ ├── JsPrinterAbstract.php
│ └── JsPrinterInterface.php
│ ├── Printer
│ ├── SourceWriter.php
│ └── SourceWriterInterface.php
│ └── lib
│ ├── js
│ ├── Exceptions.js
│ ├── HashArray.js
│ ├── JsArray.js
│ ├── JsObject.js
│ ├── classManager.js
│ └── phpJsCore.js
│ └── php
│ ├── HashArray.php
│ ├── JsArray.php
│ └── JsObject.php
├── phpunit.xml.dist
├── public_html
├── .htaccess
├── beautify.js
├── convert.php
├── favicon.ico
├── getTemplate.php
├── index.php
└── scripts.php
└── test
├── PhpToJs
└── PhpToJsTest.php
└── code
└── jsPrinter
├── jsSrc
├── generated
│ └── JsPrinter
│ │ ├── AssigningThis.js.php.js
│ │ ├── JsArray.js.php.js
│ │ ├── JsClass.js.php.js
│ │ ├── anonymousClass.js.php.js
│ │ ├── array.js.php.js
│ │ ├── class.js.php.js
│ │ ├── closure.js.php.js
│ │ ├── continue.js.php.js
│ │ ├── defaultArg.js.php.js
│ │ ├── function.js.php.js
│ │ ├── include.js.php.js
│ │ ├── includeIt.php.js
│ │ ├── inheritance.js.php.js
│ │ ├── list.js.php.js
│ │ ├── loops.js.php.js
│ │ ├── magicConstants.js.php.js
│ │ ├── magicMethods.js.php.js
│ │ ├── multiline.js.php.js
│ │ ├── namespaces.js.php.js
│ │ ├── operators.js.php.js
│ │ ├── private.js.php.js
│ │ ├── switchStatement.js.php.js
│ │ └── tryCatch.js.php.js
├── php.js
└── runTest.js
└── phpSrc
├── JsPrinter
└── array.js.php
├── global
├── AssigningThis.js.php
├── JsArray.js.php
├── JsClass.js.php
├── anonymousClass.js.php
├── array.js.php
├── class.js.php
├── closure.js.php
├── continue.js.php
├── defaultArg.js.php
├── function.js.php
├── include.js.php
├── includeIt.php
├── inheritance.js.php
├── list.js.php
├── loops.js.php
├── magicConstants.js.php
├── magicMethods.js.php
├── multiline.js.php
├── namespaces.js.php
├── operators.js.php
├── phpCore.js.php
├── private.js.php
├── switchStatement.js.php
└── tryCatch.js.php
└── runTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | .gitignore
3 | .idea/
4 | vendor
5 | /dev/
6 | /test/code/jsPrinter/jsSrc/generated/JsPrinter/
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Jozef Mostka
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PHP to JavaScript convertor
2 | ===================
3 | #### See playground: [Online Convertor](http://phptojs.orava.sk/) ####
4 |
5 | #### Suports ####
6 | - Namespaces, use
7 | - Class, abstract class
8 | - extends and interfaces
9 | - constants and define
10 | - Exceptions and catch
11 | - continue ,break
12 | - anonymous classes
13 | - magic constants
14 | - list()
15 | - magic methods __get __set and __call (only in ES6 [see Proxy in compatibility table](https://kangax.github.io/compat-table/es6/#test-Proxy))
16 | - private methods and properties (only in ES6 [see WeakMap in compatibility table](https://kangax.github.io/compat-table/es6/#test-WeakMap))
17 |
18 | #### Planed ####
19 | - include and require
20 | - class generation
21 | - yield
22 |
23 | #### Limitations ####
24 | Its there more differences between PHP and JS. Array in PHP is asociate, but in JS is not.
25 | For that reason you can use [```jsphp\JsArray```](https://github.com/tito10047/PHP-to-Javascript/blob/master/test/code/jsPrinter/phpSrc/global/JsArray.js.php) wich has same funcionality as build in JS Array.
26 |
27 | In JS you have object wich is similarly to PHP arrays, but there is diferent ordering. Also is not working
28 | with builtin php functions for manipulating with arrays. So if you need this object, wich working with
29 | foreach loop, the you can use [```jsphp\JsObject```](https://github.com/tito10047/PHP-to-Javascript/blob/master/test/code/jsPrinter/phpSrc/global/JsClass.js.php). This object has same funcionality as
30 | JsObject. But if you want extend it, your extended object cant have public or protected members, just use it, but not declare it.
31 |
32 | If you need some like associated array you can also use [```jsphp\HashArray```](https://github.com/tito10047/PHP-to-Javascript/blob/master/test/code/jsPrinter/phpSrc/JsPrinter/array.js.php)
33 |
34 | You can't define class constant and static properties with same name. in JS will be override.
35 |
36 | #### Not suport ####
37 | - trait
38 | - goto
39 | - declare(ticks)
40 |
41 | Usage
42 | ===================
43 | ```php
44 | $parser = (new \PhpParser\ParserFactory())->create(\PhpParser\ParserFactory::PREFER_PHP7);
45 | $jsPrinter = new \phptojs\JsPrinter\JsPrinter();
46 |
47 | $phpCode = file_get_contents('path/to/phpCode');
48 | $stmts = $parser->parse($phpCode);
49 | $jsCode = $jsPrinter->jsPrint($stmts);
50 | ```
51 | ----
52 | ### Use auto converter ###
53 | You can create file watcher for auto generation js script from your php
54 | code when is saved.
55 |
56 | #### PHPStorm ####
57 | go to `File/Setting/Tools/File Watchers` add custom watcher and set
58 |
59 | - File type: PHP
60 | - Scope: Create new scope to your php scripts to convert
61 | - Program: chose yor location to `php.exe`
62 | - Arguments:
63 | - `-f`
64 | - `$ProjectFileDir$/../PHP-to-Javascript/bin/phpstormWatcher.php`
65 | - `$FileName$`
66 | - `$ProjectFileDir$/phpJs` php scripts to generate
67 | - `$ProjectFileDir$/public/js/phpjs` output directory
68 | - `[-p]` enable support of private properties and method. If is disabled, all private fields is converted as public
69 |
70 | - Output paths to refresh: `$ProjectFileDir$/public/js/phpjs`
71 |
72 |
73 |
74 | Example
75 | ===================
76 |
77 | ```php
78 | interface FooInt{
79 | function fooIntFunc1($a, $b = 5);
80 | }
81 |
82 | abstract class FooAbs implements FooInt
83 | {
84 | abstract function fooAbsFunc1($a, $b);
85 |
86 | function fooAbsFunc2($a, $b){
87 | return $a + $b + 10;
88 | }
89 | }
90 |
91 | class FooParent extends FooAbs{
92 | public $foo = 5;
93 |
94 | public function __construct() {
95 | $this->foo=5;
96 | }
97 |
98 | function fooAbsFunc1($a, $b){
99 | parent::fooAbsFunc2(1,5);
100 | return $a + $b;
101 | }
102 |
103 | function fooIntFunc1($a, $b = 5){
104 | return $a + $b + 5;
105 | }
106 |
107 | public static function fooStatic(){
108 | return 10;
109 | }
110 | }
111 |
112 | class FooChild extends FooParent
113 | {
114 | public $foo = 6;
115 |
116 | function fooIntFunc1($a, $b = 5){
117 | return $a + $b;
118 | }
119 |
120 | function testParent(){
121 | assert_($this->fooIntFunc1(5, 5), 10, 'testParent 1');
122 | assert_(parent::fooIntFunc1(5, 5), 15, 'testParent 2');
123 | }
124 | }
125 |
126 | $fooParent = new FooParent();
127 | $fooChild = new FooChild();
128 |
129 | assert_($fooParent instanceof FooParent, true, 'fooParent instanceof FooParent');
130 | assert_($fooParent instanceof FooInt, true, 'fooParent instanceof FooInt');
131 |
132 | assert_($fooChild instanceof FooChild, true, 'fooChild instanceof FooChild');
133 | assert_($fooChild instanceof FooParent, true, 'fooChild instanceof FooParent');
134 | assert_($fooChild instanceof FooAbs, true, 'fooChild instanceof FooAbs');
135 | assert_($fooChild instanceof FooInt, true, 'fooChild instanceof FooInt');
136 |
137 | assert_(FooChild::fooStatic(),10, "FooChild::fooStatic()");
138 |
139 | $fooChild->testParent();
140 | ```
141 |
142 | Is converted to
143 | ```javascript
144 | var FooInt = (function() {
145 | function FooInt() {
146 | window.__IS_INHERITANCE__ = false;
147 | __INTERFACE_NEW__();
148 | }
149 | FooInt.prototype.fooIntFunc1 = function(a, b) {
150 | __INTERFACE_FUNC__();
151 | };
152 | return FooInt;
153 | })();
154 | var FooAbs = (function() {
155 | function FooAbs() {
156 | window.__IS_INHERITANCE__ = false;
157 | }
158 | __extends(FooAbs, null, arguments[1]);
159 | FooAbs.prototype.__isAbstract__ = true;
160 | FooAbs.prototype.fooAbsFunc1 = function(a, b) {
161 | __ABSTRACT_FUNC__();
162 | };
163 | FooAbs.prototype.fooAbsFunc2 = function(a, b) {
164 | return a + b + 10;
165 | };
166 | return FooAbs;
167 | })(null, [FooInt]);
168 | var FooParent = (function(parent) {
169 | function FooParent() {
170 | var __isInheritance = __IS_INHERITANCE__;
171 | window.__IS_INHERITANCE__ = true;
172 | parent.call(this);
173 | this.foo = 5;
174 | if (__isInheritance == false) {
175 | this.__construct();
176 | }
177 | }
178 | __extends(FooParent, parent);
179 | FooParent.prototype.__construct = function() {
180 | this.foo = 5;
181 | };
182 | FooParent.prototype.fooAbsFunc1 = function(a, b) {
183 | parent.prototype.fooAbsFunc2.call(this, 1, 5);
184 | return a + b;
185 | };
186 | FooParent.prototype.fooIntFunc1 = function(a, b) {
187 | if (typeof b == 'undefined') b = 5;
188 | return a + b + 5;
189 | };
190 | FooParent.fooStatic = function() {
191 | return 10;
192 | };
193 | return FooParent;
194 | })(FooAbs);
195 | var FooChild = (function(parent) {
196 | function FooChild() {
197 | window.__IS_INHERITANCE__ = true;
198 | parent.call(this);
199 | this.foo = 6;
200 | }
201 | __extends(FooChild, parent);
202 | FooChild.prototype.fooIntFunc1 = function(a, b) {
203 | if (typeof b == 'undefined') b = 5;
204 | return a + b;
205 | };
206 | FooChild.prototype.testParent = function() {
207 | assert_(this.fooIntFunc1(5, 5), 10, 'testParent 1');
208 | assert_(parent.prototype.fooIntFunc1.call(this, 5, 5), 15, 'testParent 2');
209 | };
210 | return FooChild;
211 | })(FooParent);
212 | var fooParent;
213 | fooParent = new FooParent();
214 | var fooChild;
215 | fooChild = new FooChild();
216 | assert_(fooParent instanceof FooParent, true, 'fooParent instanceof FooParent');
217 | assert_(fooParent instanceof FooInt, true, 'fooParent instanceof FooInt');
218 | assert_(fooChild instanceof FooChild, true, 'fooChild instanceof FooChild');
219 | assert_(fooChild instanceof FooParent, true, 'fooChild instanceof FooParent');
220 | assert_(fooChild instanceof FooAbs, true, 'fooChild instanceof FooAbs');
221 | assert_(fooChild instanceof FooInt, true, 'fooChild instanceof FooInt');
222 | assert_(FooChild.fooStatic(), 10, 'FooChild::fooStatic()');
223 | fooChild.testParent();
224 | ```
225 | [More Examples](https://github.com/tito10047/PHP-to-Javascript/tree/master/test/code/jsPrinter/jsSrc/generated/JsPrinter)
226 |
--------------------------------------------------------------------------------
/bin/phpstormWatcher.php:
--------------------------------------------------------------------------------
1 | create(\PhpParser\ParserFactory::PREFER_PHP7);
60 | $jsPrinter = new \phptojs\JsPrinter\JsPrinter($usePrivate);
61 |
62 | $stmts = $parser->parse($phpContent);
63 | ob_start();
64 | $jsCode = $jsPrinter->jsPrint($stmts);
65 | $errors = ob_get_clean();
66 | $errors = explode(PHP_EOL, $errors);
67 | foreach ($errors as $error) {
68 | if ($error != "") {
69 | $errorCount++;
70 | echo "Warning: " . $error.PHP_EOL;
71 | }
72 | }
73 | foreach ($jsPrinter->getErrors() as $error) {
74 | $errorCount++;
75 | echo "Warning: " . $error . PHP_EOL;
76 | }
77 | $dotPos = strrpos($phpFilename,".");
78 | $phpFilenameWithoutExtension = substr($phpFilename,0,$dotPos);
79 | $filePathWithoutExtension = substr($phpFilenameWithoutExtension,strlen($phpRootDir));
80 | if (in_array(substr($filePathWithoutExtension,0,1),["/","\\"])){
81 | $filePathWithoutExtension = substr($filePathWithoutExtension,1);
82 | }
83 | $jsFileName = realpath($jsRootDir).DIRECTORY_SEPARATOR.$filePathWithoutExtension.".js";
84 | $parentDir = dirname($jsFileName);
85 | if (!file_exists($parentDir)){
86 | mkdir($parentDir,777,true);
87 | }
88 | $jsCode = "/**
89 | * File generated by PHP to JS converter
90 | * Don't modify this file because changes will be lost
91 | */
92 | ".$jsCode;
93 |
94 | $sourceCode=$jsCode;
95 | require_once __DIR__.'/scriptformatter.php';
96 | $jsCode=$sourceCode;
97 |
98 | file_put_contents($jsFileName, $jsCode);
99 |
100 | } catch (PhpParser\Error $e) {
101 | echo 'ERROR:', $e->getMessage();
102 | $errorCount++;
103 | } catch (Exception $e) {
104 | echo "ERROR:Some is wrong".PHP_EOL.$e->getMessage();
105 | $errorCount++;
106 | }
107 | if ($errorCount>0){
108 | exit(1);
109 | }
110 | $libDir = $jsRootDir.DIRECTORY_SEPARATOR.'lib';
111 | if (!file_exists($libDir)){
112 | mkdir($libDir);
113 | }
114 | $libFiles = glob(JS_LIB_DIR.DIRECTORY_SEPARATOR.'*');
115 | foreach ($libFiles as $file){
116 | $name = substr($file,strlen(JS_LIB_DIR));
117 | $exportName = $libDir.DIRECTORY_SEPARATOR.$name;
118 | if (file_exists($exportName) && filemtime($exportName)>=filemtime($file)){
119 | continue;
120 | }
121 | copy($file,$exportName);
122 | }
--------------------------------------------------------------------------------
/bin/scriptformatter.php:
--------------------------------------------------------------------------------
1 | $len){
31 | $len=mb_strlen($s);
32 | }
33 | }
34 | while($index>0){
35 | $char=mb_substr($str,--$index,1);
36 | $subStr=$char.$subStr;
37 | if (mb_strlen($subStr)>$len){
38 | $subStr=mb_substr($subStr,0,$len);
39 | }
40 | foreach($search as $s){
41 | if ($s==mb_substr($subStr,mb_strlen($subStr)-mb_strlen($s))){
42 | return true;
43 | }
44 | }
45 | if (in_array($char,$endChar)){
46 | return false;
47 | }
48 | }
49 | return false;
50 | }
51 | }
52 | $debug=false;
53 | $start = time();
54 | $url = isset( $_GET['url'] ) ? $_GET['url'] : '';
55 |
56 | if( isset($sourceCode) && $sourceCode ) {
57 | $code = $sourceCode;
58 | // $code = str_replace("\r\n","\n",$code);
59 | $code = preg_replace('~(*BSR_ANYCRLF)\R~', "\n", $code);
60 | }else{
61 | /* enable command line usage */
62 | if (!isset($source_file) || !$source_file) {
63 | $source_file=(isset($argv[1]) && is_file($argv[1])) ? $argv[1] : '';
64 | }
65 | $code = file_get_contents($source_file);
66 | }
67 |
68 |
69 | // echo ( ' loaded stuff in ' . (time() - $start) . "ms\n");
70 |
71 | if( empty( $code ) ){echo( 'could not retrieve '.$url ); exit;}
72 |
73 | $output=''; /* would array perform better as in JS? */
74 | $num_indents = 0;
75 | $character_index = 0;
76 | $char='';
77 |
78 | $scope = CODE;
79 | $before_escape_scope=0;
80 | $at_start_of_statement_or_expression=true; /* used to distinguish divisor from regexp literal */
81 | $last_complete_word = ''; /* some rudimentary tokenisation is required for the divisor-or-regexp problem */
82 | $statement_words = array('return', 'typeof', 'instanceof', 'break', 'continue', 'delete', 'in', 'new', 'throw');
83 |
84 | while( $character_index < strlen( $code ) ){
85 | $char = mb_substr( $code, $character_index, 1 );
86 | if($debug)echo ( (time() - $start). "ms elapsed, now on $character_index / ".strlen($code)." $char, mode: $scope, last word: '$last_complete_word' - start of expression? $at_start_of_statement_or_expression \n");
87 | $pre = ''; /* add this string *before* this character when constructing output */
88 | $post = ''; /* add this string *after* this character when constructing output */
89 | switch( $char ){
90 | case '"': /* double quote */
91 | switch( $scope ){
92 | case STRING_DBL:
93 | $scope=CODE ; break; /* a non-escaped quote inside string terminates string */
94 | case ESCAPE:
95 | $scope = $before_escape_scope; break; /* the quote was escaped, return to previous scope */
96 | case CODE:
97 | $scope = STRING_DBL ; /* start-of-string double quote */
98 | $at_start_of_statement_or_expression=false;
99 | }
100 | break;
101 | case '\'': /* single quote */
102 | switch( $scope ){
103 | case STRING_SGL:
104 | $scope=CODE ; break; /* a non-escaped quote inside string terminates string */
105 | case ESCAPE:
106 | $scope = $before_escape_scope; break; /* the quote was escaped, return to previous scope */
107 | case CODE:
108 | $scope = STRING_SGL ; /* start-of-string single quote */
109 | $at_start_of_statement_or_expression=false;
110 | }
111 | break;
112 | case '\\':
113 | if( $scope == STRING_DBL || $scope == STRING_SGL || $scope == REGEXP || $scope == REGEXP_CHAR_CLASS){
114 | $before_escape_scope = $scope ;
115 | $scope = ESCAPE ; /* next character not to be taken seriously (well..) */
116 | }else if( $scope == ESCAPE ){ /* handle escaped backslashes "\\" */
117 | $scope = $before_escape_scope ;
118 | }
119 | break;
120 | case '/':
121 | if( $scope == CODE ){ /* lookahead: start of comment or something else? */
122 | $tmp = mb_substr( $code, $character_index+1, 1 );
123 | if( $tmp == '*' ){ /* start of multi-line comment */
124 | $scope = MULTI_LINE_COMMENT ;
125 | }else if( $tmp == '/' ){ /* start of single-line comment */
126 | $scope = SINGLE_LINE_COMMENT ;
127 | }else if( $at_start_of_statement_or_expression || in_array( $last_complete_word, $statement_words ) ){ /* start of regexp */
128 | $scope = REGEXP ;
129 | }
130 | }else if( $scope == ESCAPE ){
131 | $scope = $before_escape_scope ;
132 | }else if( $scope == REGEXP ){
133 | $scope = CODE ;
134 | }else if( $scope == MULTI_LINE_COMMENT ){ /* time to leave the comment?? */
135 | $tmp = mb_substr( $code, $character_index - 1, 1 );
136 | if( $tmp == '*' ) {
137 | $scope = CODE ;
138 | $post = "\n";
139 | $post .= str_repeat( "\t", $num_indents );
140 | } /* we only enter multi-line-comment mode from CODE scope AFAIK */
141 | }
142 | break;
143 | case '{':
144 | if( $scope == CODE ){ /* start-of-block curly brace */
145 | /* Sigbj�rn special: do not wrap and indent empty blocks (object literal) */
146 | if( lookahead( $code, $character_index, true )=='}' ){ /* we have an object literal. We'll simply add a closing brace and jump ahead */
147 | $character_index=strpos( $code, '}', $character_index );
148 | $t = str_repeat("\t", $num_indents);
149 | $post="};\n{$t}";
150 | break;
151 | }
152 | $num_indents ++ ;
153 | $post = "\n";
154 | $post .= str_repeat( "\t", $num_indents );
155 | $at_start_of_statement_or_expression = true;
156 | }else if( $scope == ESCAPE ){
157 | $scope = $before_escape_scope ;
158 | }
159 | break;
160 | case '}':
161 | if( $scope == CODE ){ /* end-of-block curly brace */
162 | if( $num_indents>0 )$num_indents -- ;
163 | $tmp = mb_substr( $code, $character_index +1, 1 );
164 | $pre="\n";
165 | $pre.=str_repeat("\t", $num_indents);
166 | if ($tmp!=";") {
167 | $post="\n".str_repeat("\t", $num_indents);
168 | }
169 | }else if( $scope == ESCAPE ){
170 | $scope = $before_escape_scope ;
171 | }
172 | break;
173 | case ';':
174 | if( $scope == CODE ){ /* end-of-statement semicolon //, or between-variables comma */
175 | if (!lookReverseForTo($code,$character_index,"for","\n")) {
176 | $post="\n";
177 | $post.=str_repeat("\t", $num_indents);
178 | $at_start_of_statement_or_expression=true;
179 | }
180 | }else if( $scope == ESCAPE ){
181 | $scope = $before_escape_scope ;
182 | }
183 | break;
184 | case "\n":
185 | if( $scope == SINGLE_LINE_COMMENT ){
186 | $scope = CODE; /* we only enter SINGLE_LINE_COMMENT mode from CODE, right? */
187 | }else if( $scope == ESCAPE ){
188 | $scope = $before_escape_scope ;
189 | } /* no break, we want to get to the $at_start_of_statement_or_expression bit below */
190 | case '(':
191 | case '!':
192 | case '=':
193 | case '-':
194 | case '+':
195 | case '?':
196 | case '*':
197 | case '&':
198 | case ':':
199 | case ',':
200 | case '|':
201 | if($char=="*" & $scope == MULTI_LINE_COMMENT ) { /* time to leave the comment?? */
202 | $tmp=mb_substr($code, $character_index-1, 3);
203 | if ($tmp==' * ' || $tmp==" */") {
204 | $pre="\n".str_repeat( "\t", $num_indents )." ";
205 | }
206 | }else if( $scope == CODE ){
207 | $at_start_of_statement_or_expression=true; /* at start of parens, after equal sign etc.. if the next char is a forward slash it will be a start-of-regexp, not a divisor */
208 | }else if( $scope == ESCAPE ){
209 | $scope = $before_escape_scope ;
210 | }
211 | break;
212 | case '[':
213 | if( $scope == REGEXP ){
214 | $scope=REGEXP_CHAR_CLASS;
215 | $at_start_of_statement_or_expression=false;
216 | }else if( $scope == ESCAPE ){
217 | $scope = $before_escape_scope ;
218 | }
219 | break;
220 | case ']':
221 | if( $scope == REGEXP_CHAR_CLASS ){
222 | $scope=REGEXP;
223 | $at_start_of_statement_or_expression=false;
224 | }else if( $scope == ESCAPE ){
225 | $scope = $before_escape_scope ;
226 | }
227 | break;
228 | default:
229 | if( $scope == ESCAPE ){
230 | // $scope = $before_escape_scope ; /* always drop out of escape mode on next character.. yes, multi-char escapes exist but it's OK to treat the rest of it as part of the string */
231 | }
232 | if( $scope == CODE ){ /* reset at_start_of_statement_or_expression flag (but ignore whitespace!) */
233 | if( !( $char==' ' || $char=="\t" ) ) $at_start_of_statement_or_expression = false;
234 | }
235 | // if ($char=="\t" ) {
236 | // $character_index++;
237 | // continue 2;
238 | // }
239 | }
240 | if( preg_match('/[a-zA-Z0-9]/', $char) ){
241 | /* if the previous character was whitespace or punctuation, this starts a new word.. */
242 | if( ! preg_match('/[a-zA-Z0-9]/', mb_substr( $code, $character_index - 1, 1 )) ){
243 | $last_complete_word='';
244 | }
245 | $last_complete_word .= $char;
246 | }
247 | if( ($scope == CODE) && ( $char == "\t" || $char == "\n" ) ){ /* this script will add formatting whitespace */ // proven too fragile..
248 |
249 | }else{
250 | $output .= $pre . $char . $post ;
251 | }
252 | $character_index++;
253 | }
254 | $output = preg_replace( '/\n\s*\n/', "\n", $output );
255 | if(!isset($sourceCode) || !$sourceCode){
256 | $f=fopen($source_file, 'w');
257 | fwrite($f, $output);
258 | fclose($f);
259 | echo $source_file;
260 | }else{
261 | $sourceCode=$output;
262 | }
263 |
264 | ?>
265 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mostka/phptojs",
3 | "description": "Php to JS converter convert PHP code into JavaScript. Support Classes, namespaces, inheritance and more. ",
4 | "keywords": ["converter"],
5 | "minimum-stability": "dev",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Jozef Môstka",
10 | "email": "jozef@mostka.com"
11 | }
12 | ],
13 | "autoload": {
14 | "psr-4": {
15 | "phptojs\\": "lib/phptojs"
16 | }
17 | },
18 | "require": {
19 | "php": "^5.3.3 || ^7.0",
20 | "nikic/php-parser": "3.x"
21 | },
22 | "require-dev": {
23 | "phpunit/phpunit": "5.3.4"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/bootstrap.php:
--------------------------------------------------------------------------------
1 | ' . PHP_EOL);
12 | define('JS_SCRIPT_END', '');
13 |
14 |
15 | use PhpParser\Node;
16 | use PhpParser\Node\Expr;
17 | use PhpParser\Node\Expr\AssignOp;
18 | use PhpParser\Node\Expr\BinaryOp;
19 | use PhpParser\Node\Expr\Cast;
20 | use PhpParser\Node\Name;
21 | use PhpParser\Node\Scalar;
22 | use PhpParser\Node\Scalar\MagicConst;
23 | use PhpParser\Node\Stmt;
24 | use PhpParser\ParserFactory;
25 | use PhpParser\PrettyPrinterAbstract;
26 | use PhpToJs\Printer\SourceWriter;
27 |
28 | abstract class JsPrinterAbstract extends PrettyPrinterAbstract {
29 | public static $enableVariadic = false;
30 | /** @var SourceWriter */
31 | protected $writer;
32 |
33 | protected $ROOT_PATH_FROM = null;
34 | protected $ROOT_PATH_TO = null;
35 | protected $ROOT_PATH_TO_EXT = null;
36 | protected $isOnlyJsFile = false;
37 |
38 | /**
39 | * @return array
40 | */
41 | public function getErrors() {
42 | return $this->errors;
43 | }
44 |
45 | public static $throwErrors = true;
46 | protected $errors = [];
47 |
48 | protected function notImplemented($expression, $message, $throw = false) {
49 | if ($expression) {
50 | $msg = "not implemented " . $message;
51 | $this->errors[] = $msg;
52 | if ($throw) {
53 | if (self::$throwErrors == true) {
54 | throw new \RuntimeException($msg);
55 | } else {
56 | }
57 | }
58 | }
59 | }
60 |
61 | /**
62 | * Pretty prints a file of statements (includes the opening ROOT_PATH_FROM = dirname($filePath) . DIRECTORY_SEPARATOR;
70 | $this->isOnlyJsFile = $isOnlyJsFile;
71 |
72 | $code = file_get_contents($filePath);
73 |
74 | $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
75 | $stmts = $parser->parse($code);
76 |
77 | $code = $this->jsPrint($stmts);
78 |
79 | if (!$isOnlyJsFile) {
80 | $code = JS_SCRIPT_BEGIN . $code . JS_SCRIPT_END;
81 | }
82 | $code = str_replace(JS_SCRIPT_BEGIN . JS_SCRIPT_END, "", $code);
83 |
84 | return $code;
85 | }
86 |
87 | public function jsPrintFileTo($filePath, $dstFilePath) {
88 | $this->ROOT_PATH_TO = dirname($dstFilePath) . DIRECTORY_SEPARATOR;
89 | $this->ROOT_PATH_TO_EXT = pathinfo($dstFilePath, PATHINFO_EXTENSION);
90 | $isOnlyJsFile = $this->ROOT_PATH_TO_EXT == 'js';
91 | $code = $this->jsPrintFile($filePath, $isOnlyJsFile);
92 | if (!file_exists(dirname($dstFilePath))) {
93 | mkdir(dirname($dstFilePath), 0777, true);
94 | }
95 | return file_put_contents($dstFilePath, $code);
96 | }
97 |
98 | /**
99 | * Pretty prints a node.
100 | *
101 | * @param Node $node Node to be pretty printed
102 | *
103 | * @return void
104 | */
105 | protected function p(Node $node) {
106 | $this->{'p' . $node->getType()}($node);
107 | }
108 |
109 | /**
110 | * Pretty prints an array of statements.
111 | *
112 | * @param Node[] $stmts Array of statements
113 | *
114 | * @return string Pretty printed statements
115 | */
116 | public function jsPrint(array $stmts) {
117 | $this->pStmts($stmts, false);
118 | return $this->writer->getResetCode();
119 | }
120 |
121 |
122 | /**
123 | * @see PhpParser\Printer\PrinterAbstract::pStmts
124 | * @param array $nodes
125 | * @param bool $indent
126 | * @return string|void
127 | */
128 | protected function pStmts(array $nodes, $indent = true) {
129 | foreach ($nodes as $node) {
130 | $comments=$node->getAttribute('comments', array());
131 | if ($comments && !($node instanceof Stmt\ClassMethod || $node instanceof Stmt\ClassConst)){
132 | $this->pComments($comments);
133 | }
134 |
135 | $this->writer->pushDelay();
136 | $this->p($node);
137 | $this->writer->popDelayToVar($stmts);
138 |
139 | $this->printVarDef();
140 | $this->printUseByRefDef();
141 | $this->writer->print_($stmts);
142 |
143 | $this->writer->println($node instanceof Node\Expr ? ';' : '');
144 | }
145 | }
146 |
147 | abstract protected function printUseByRefDef();
148 |
149 | abstract protected function printVarDef();
150 |
151 | /**
152 | * @param \PhpParser\Comment[] $comments
153 | * @return void
154 | * @return string
155 | */
156 | protected function pComments(array $comments) {
157 | foreach ($comments as $comment) {
158 | $comment=$comment->getReformattedText();
159 | $comment=preg_replace('/(@(param|var) )([\w\|\\\\]+)( \$\w*)?/',"$1{\$3}$4",$comment);
160 | $comment=preg_replace('/(@(param|var) )({[\w\|\\\\]*} )?\$(\w*)/',"$1$3$4",$comment);
161 | $comment=str_replace(["@var","{\\","\\"],["@type","{N.","."],$comment);
162 | $this->writer->println($comment);
163 | }
164 | }
165 |
166 | /**
167 | * @see PhpParser\Printer\PrinterAbstract::pInfixOp
168 | * @param $type
169 | * @param \PhpParser\Node $leftNode
170 | * @param string|int $operator or delayId
171 | * @param \PhpParser\Node $rightNode
172 | * @return void
173 | */
174 | protected function pInfixOp($type, Node $leftNode, $operator, Node $rightNode) {
175 | list($precedence, $associativity) = $this->precedenceMap[$type];
176 |
177 | $this->pPrec($leftNode, $precedence, $associativity, -1);
178 | if (gettype($operator) == "integer") {
179 | $this->writer->writeDelay($operator);
180 | } else {
181 | $this->writer->print_($operator);
182 | }
183 | $this->pPrec($rightNode, $precedence, $associativity, 1);
184 | }
185 |
186 | /**
187 | * @see PhpParser\Printer\PrinterAbstract::pPrefixOp
188 | * @param $type
189 | * @param $operatorString
190 | * @param \PhpParser\Node $node
191 | * @return void
192 | */
193 | protected function pPrefixOp($type, $operatorString, Node $node) {
194 | list($precedence, $associativity) = $this->precedenceMap[$type];
195 | $this->writer->print_($operatorString);
196 | $this->pPrec($node, $precedence, $associativity, 1);
197 | }
198 |
199 | /**
200 | * @see PhpParser\Printer\PrinterAbstract::pPostfixOp
201 | * @param $type
202 | * @param \PhpParser\Node $node
203 | * @param $operatorString
204 | * @return void
205 | */
206 | protected function pPostfixOp($type, Node $node, $operatorString) {
207 | list($precedence, $associativity) = $this->precedenceMap[$type];
208 | $this->pPrec($node, $precedence, $associativity, -1);
209 | $this->writer->print_($operatorString);
210 | }
211 |
212 | /**
213 | * @see PhpParser\Printer\PrinterAbstract::pPrec
214 | * @param \PhpParser\Node $node
215 | * @param int $parentPrecedence
216 | * @param int $parentAssociativity
217 | * @param int $childPosition
218 | * @return void
219 | */
220 | protected function pPrec(Node $node, $parentPrecedence, $parentAssociativity, $childPosition) {
221 | $type = $node->getType();
222 | if (isset($this->precedenceMap[$type])) {
223 | $childPrecedence = $this->precedenceMap[$type][0];
224 | if ($childPrecedence > $parentPrecedence ||
225 | ($parentPrecedence == $childPrecedence && $parentAssociativity != $childPosition)
226 | ) {
227 | $this->writer->print_("(");
228 | $this->{'p' . $type}($node);
229 | $this->writer->print_(")");
230 | return;
231 | }
232 | }
233 |
234 | $this->{'p' . $type}($node);
235 | }
236 |
237 | /**
238 | * @param array $nodes
239 | * @param string $glue
240 | * @return void
241 | */
242 | protected function pImplode(array $nodes, $glue = '') {
243 | $l = count($nodes);
244 | for ($i = 0; $i < $l; $i++) {
245 | $node = $nodes[$i];
246 | $this->p($node);
247 | if ($i < $l - 1) {
248 | $this->writer->print_($glue);
249 | }
250 | }
251 | }
252 |
253 |
254 | public function pScalar_LNumber(Scalar\LNumber $node) {
255 | $this->print_((string)$node->value);
256 | }
257 |
258 | public function pScalar_DNumber(Scalar\DNumber $node) {
259 | $stringValue = (string)$node->value;
260 | if ($stringValue == 'INF') {
261 | $stringValue = 'Infinity';
262 | }
263 | // ensure that number is really printed as float
264 | $stringValue = ctype_digit($stringValue) ? $stringValue . '.0' : $stringValue;
265 | $this->print_($stringValue);
266 | }
267 |
268 | public function pExpr_Assign(Expr\Assign $node) {
269 | $this->pInfixOp('Expr_Assign', $node->var, ' = ', $node->expr);
270 | }
271 |
272 | public function pExpr_AssignRef(Expr\AssignRef $node) {
273 | $this->pInfixOp('Expr_AssignRef', $node->var, ' =& ', $node->expr);
274 | }
275 |
276 | public function pExpr_AssignOp_Plus(AssignOp\Plus $node) {
277 | $this->pInfixOp('Expr_AssignOp_Plus', $node->var, ' += ', $node->expr);
278 | }
279 |
280 | public function pExpr_AssignOp_Minus(AssignOp\Minus $node) {
281 | $this->pInfixOp('Expr_AssignOp_Minus', $node->var, ' -= ', $node->expr);
282 | }
283 |
284 | public function pExpr_AssignOp_Mul(AssignOp\Mul $node) {
285 | $this->pInfixOp('Expr_AssignOp_Mul', $node->var, ' *= ', $node->expr);
286 | }
287 |
288 | public function pExpr_AssignOp_Div(AssignOp\Div $node) {
289 | $this->pInfixOp('Expr_AssignOp_Div', $node->var, ' /= ', $node->expr);
290 | }
291 |
292 | public function pExpr_AssignOp_Concat(AssignOp\Concat $node) {
293 | $this->pInfixOp('Expr_AssignOp_Concat', $node->var, ' += ', $node->expr);
294 | }
295 |
296 | public function pExpr_AssignOp_Mod(AssignOp\Mod $node) {
297 | $this->pInfixOp('Expr_AssignOp_Mod', $node->var, ' %= ', $node->expr);
298 | }
299 |
300 | public function pExpr_AssignOp_BitwiseAnd(AssignOp\BitwiseAnd $node) {
301 | $this->pInfixOp('Expr_AssignOp_BitwiseAnd', $node->var, ' &= ', $node->expr);
302 | }
303 |
304 | public function pExpr_AssignOp_BitwiseOr(AssignOp\BitwiseOr $node) {
305 | $this->pInfixOp('Expr_AssignOp_BitwiseOr', $node->var, ' |= ', $node->expr);
306 | }
307 |
308 | public function pExpr_AssignOp_BitwiseXor(AssignOp\BitwiseXor $node) {
309 | $this->pInfixOp('Expr_AssignOp_BitwiseXor', $node->var, ' ^= ', $node->expr);
310 | }
311 |
312 | public function pExpr_AssignOp_ShiftLeft(AssignOp\ShiftLeft $node) {
313 | $this->pInfixOp('Expr_AssignOp_ShiftLeft', $node->var, ' <<= ', $node->expr);
314 | }
315 |
316 | public function pExpr_AssignOp_ShiftRight(AssignOp\ShiftRight $node) {
317 | $this->pInfixOp('Expr_AssignOp_ShiftRight', $node->var, ' >>= ', $node->expr);
318 | }
319 |
320 | public function pExpr_AssignOp_Pow(AssignOp\Pow $node) {//TODO: implement this
321 | $this->pInfixOp('Expr_AssignOp_Pow', $node->var, ' **= ', $node->expr);
322 | }
323 |
324 | // Binary expressions
325 |
326 | public function pExpr_BinaryOp_Plus(BinaryOp\Plus $node) {
327 | $this->pInfixOp('Expr_BinaryOp_Plus', $node->left, ' + ', $node->right);
328 | }
329 |
330 | public function pExpr_BinaryOp_Minus(BinaryOp\Minus $node) {
331 | $this->pInfixOp('Expr_BinaryOp_Minus', $node->left, ' - ', $node->right);
332 | }
333 |
334 | public function pExpr_BinaryOp_Mul(BinaryOp\Mul $node) {
335 | $this->pInfixOp('Expr_BinaryOp_Mul', $node->left, ' * ', $node->right);
336 | }
337 |
338 | public function pExpr_BinaryOp_Div(BinaryOp\Div $node) {
339 | $this->pInfixOp('Expr_BinaryOp_Div', $node->left, ' / ', $node->right);
340 | }
341 |
342 | public function pExpr_BinaryOp_Concat(BinaryOp\Concat $node) {
343 | if ($this->isConcatString($node)){
344 | $this->p($node->left);
345 | $this->writer->print_("+");
346 | $this->p($node->right);
347 | }else {
348 | $this->pInfixOp('Expr_BinaryOp_Concat', $node->left, ' . ', $node->right);
349 | }
350 | }
351 |
352 | private function isConcatString(BinaryOp\Concat $node){
353 | if ($node->left instanceof BinaryOp\Concat){
354 | if($this->isConcatString($node->left)){
355 | return true;
356 | }
357 | }
358 | if ($node->right instanceof BinaryOp\Concat){
359 | if($this->isConcatString($node->right)){
360 | return true;
361 | }
362 | }
363 | if ($node->left instanceof Scalar\String_ || $node->right instanceof Scalar\String_){
364 | return true;
365 | }
366 | return false;
367 | }
368 |
369 | public function pExpr_BinaryOp_Mod(BinaryOp\Mod $node) {
370 | $this->pInfixOp('Expr_BinaryOp_Mod', $node->left, ' % ', $node->right);
371 | }
372 |
373 | public function pExpr_BinaryOp_BooleanAnd(BinaryOp\BooleanAnd $node) {
374 | $this->pInfixOp('Expr_BinaryOp_BooleanAnd', $node->left, ' && ', $node->right);
375 | }
376 |
377 | public function pExpr_BinaryOp_BooleanOr(BinaryOp\BooleanOr $node) {
378 | $this->pInfixOp('Expr_BinaryOp_BooleanOr', $node->left, ' || ', $node->right);
379 | }
380 |
381 | public function pExpr_BinaryOp_BitwiseAnd(BinaryOp\BitwiseAnd $node) {
382 | $this->pInfixOp('Expr_BinaryOp_BitwiseAnd', $node->left, ' & ', $node->right);
383 | }
384 |
385 | public function pExpr_BinaryOp_BitwiseOr(BinaryOp\BitwiseOr $node) {
386 | $this->pInfixOp('Expr_BinaryOp_BitwiseOr', $node->left, ' | ', $node->right);
387 | }
388 |
389 | public function pExpr_BinaryOp_BitwiseXor(BinaryOp\BitwiseXor $node) {
390 | $this->pInfixOp('Expr_BinaryOp_BitwiseXor', $node->left, ' ^ ', $node->right);
391 | }
392 |
393 | public function pExpr_BinaryOp_ShiftLeft(BinaryOp\ShiftLeft $node) {
394 | $this->pInfixOp('Expr_BinaryOp_ShiftLeft', $node->left, ' << ', $node->right);
395 | }
396 |
397 | public function pExpr_BinaryOp_ShiftRight(BinaryOp\ShiftRight $node) {
398 | $this->pInfixOp('Expr_BinaryOp_ShiftRight', $node->left, ' >> ', $node->right);
399 | }
400 |
401 | public function pExpr_BinaryOp_Pow(BinaryOp\Pow $node) {//TODO: implement this
402 | $this->pInfixOp('Expr_BinaryOp_Pow', $node->left, ' ** ', $node->right);
403 | }
404 |
405 | public function pExpr_BinaryOp_LogicalAnd(BinaryOp\LogicalAnd $node) {
406 | $this->pInfixOp('Expr_BinaryOp_LogicalAnd', $node->left, ' && ', $node->right);
407 | }
408 |
409 | public function pExpr_BinaryOp_LogicalOr(BinaryOp\LogicalOr $node) {
410 | $this->pInfixOp('Expr_BinaryOp_LogicalOr', $node->left, ' || ', $node->right);
411 | }
412 |
413 | public function pExpr_BinaryOp_LogicalXor(BinaryOp\LogicalXor $node) {//TODO: implement this
414 | $this->pInfixOp('Expr_BinaryOp_LogicalXor', $node->left, ' ^ ', $node->right);
415 | }
416 |
417 | public function pExpr_BinaryOp_Equal(BinaryOp\Equal $node) {
418 | $this->pInfixOp('Expr_BinaryOp_Equal', $node->left, ' == ', $node->right);
419 | }
420 |
421 | public function pExpr_BinaryOp_NotEqual(BinaryOp\NotEqual $node) {
422 | $this->pInfixOp('Expr_BinaryOp_NotEqual', $node->left, ' != ', $node->right);
423 | }
424 |
425 | public function pExpr_BinaryOp_Identical(BinaryOp\Identical $node) {
426 | $this->pInfixOp('Expr_BinaryOp_Identical', $node->left, ' === ', $node->right);
427 | }
428 |
429 | public function pExpr_BinaryOp_NotIdentical(BinaryOp\NotIdentical $node) {
430 | $this->pInfixOp('Expr_BinaryOp_NotIdentical', $node->left, ' !== ', $node->right);
431 | }
432 |
433 | public function pExpr_BinaryOp_Spaceship(BinaryOp\Spaceship $node) {
434 | //TODO: Implement pExpr_BinaryOp_Spaceship() method.
435 | $this->notImplemented(true, __METHOD__);
436 | }
437 |
438 | public function pExpr_BinaryOp_Greater(BinaryOp\Greater $node) {
439 | $this->pInfixOp('Expr_BinaryOp_Greater', $node->left, ' > ', $node->right);
440 | }
441 |
442 | public function pExpr_BinaryOp_GreaterOrEqual(BinaryOp\GreaterOrEqual $node) {
443 | $this->pInfixOp('Expr_BinaryOp_GreaterOrEqual', $node->left, ' >= ', $node->right);
444 | }
445 |
446 | public function pExpr_BinaryOp_Smaller(BinaryOp\Smaller $node) {
447 | $this->pInfixOp('Expr_BinaryOp_Smaller', $node->left, ' < ', $node->right);
448 | }
449 |
450 | public function pExpr_BinaryOp_SmallerOrEqual(BinaryOp\SmallerOrEqual $node) {
451 | $this->pInfixOp('Expr_BinaryOp_SmallerOrEqual', $node->left, ' <= ', $node->right);
452 | }
453 |
454 | public function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node) {
455 | // TODO: Implement pExpr_BinaryOp_Coalesce() method.
456 | $this->notImplemented(true, __METHOD__);
457 | }
458 |
459 | // Unary expressions
460 |
461 | public function pExpr_BooleanNot(Expr\BooleanNot $node) {
462 | $this->pPrefixOp('Expr_BooleanNot', '!', $node->expr);
463 | }
464 |
465 | public function pExpr_BitwiseNot(Expr\BitwiseNot $node) {//TODO: implement this
466 | $this->pPrefixOp('Expr_BitwiseNot', '~', $node->expr);
467 | }
468 |
469 | public function pExpr_UnaryMinus(Expr\UnaryMinus $node) {
470 | $this->pPrefixOp('Expr_UnaryMinus', '-', $node->expr);
471 | }
472 |
473 | public function pExpr_UnaryPlus(Expr\UnaryPlus $node) {
474 | $this->pPrefixOp('Expr_UnaryPlus', '+', $node->expr);
475 | }
476 |
477 | public function pExpr_PreInc(Expr\PreInc $node) {
478 | $this->pPrefixOp('Expr_PreInc', '++', $node->var);
479 | }
480 |
481 | public function pExpr_PreDec(Expr\PreDec $node) {
482 | $this->pPrefixOp('Expr_PreDec', '--', $node->var);
483 | }
484 |
485 | public function pExpr_PostInc(Expr\PostInc $node) {
486 | $this->pPostfixOp('Expr_PostInc', $node->var, '++');
487 | }
488 |
489 | public function pExpr_PostDec(Expr\PostDec $node) {
490 | $this->pPostfixOp('Expr_PostDec', $node->var, '--');
491 | }
492 |
493 | public function pExpr_ErrorSuppress(Expr\ErrorSuppress $node) {//TODO: implement this
494 | $this->notImplemented(true, 'ErrorSuppress by @', true);
495 | $this->pPrefixOp('Expr_ErrorSuppress', '@', $node->expr);
496 | }
497 |
498 | public function pExpr_YieldFrom(Expr\YieldFrom $node) {
499 | // TODO: Implement pExpr_YieldFrom() method.
500 | $this->notImplemented(true, __METHOD__);
501 | }
502 |
503 | public function pExpr_Print(Expr\Print_ $node) {
504 | // TODO: Implement pExpr_Print() method.
505 | $this->print_("console.log(");
506 | $this->p($node->expr);
507 | $this->print_(")");
508 | }
509 |
510 | // Casts
511 |
512 | public function pExpr_Cast_Int(Cast\Int_ $node) {
513 | $this->print_("parseInt(");
514 | $this->p($node->expr);
515 | $this->print_(")");
516 | }
517 |
518 | public function pExpr_Cast_Double(Cast\Double $node) {
519 | $this->print_("parseFloat(");
520 | $this->p($node->expr);
521 | $this->print_(")");
522 | }
523 |
524 | public function pExpr_Cast_String(Cast\String_ $node) {
525 | $this->print_("(");
526 | $this->p($node->expr);
527 | $this->print_(").toString()");
528 | }
529 |
530 | public function pExpr_Cast_Array(Cast\Array_ $node) {//TODO: implement this
531 | $this->notImplemented(true, ' conversion to (array)', true);
532 | $this->pPrefixOp('Expr_Cast_Array', '(array) ', $node->expr);
533 | }
534 |
535 | public function pExpr_Cast_Object(Cast\Object_ $node) {//TODO: implement this
536 | $this->notImplemented(true, ' conversion to (object)', true);
537 | $this->pPrefixOp('Expr_Cast_Object', '(object) ', $node->expr);
538 | }
539 |
540 | public function pExpr_Cast_Bool(Cast\Bool_ $node) {
541 | return "Boolean(" . $this->p($node->expr) . ")";
542 | }
543 |
544 | public function pExpr_Cast_Unset(Cast\Unset_ $node) {//TODO: implement this
545 | $this->notImplemented(true, __METHOD__);
546 | $this->pPrefixOp('Expr_Cast_Unset', 'delete ', $node->expr);
547 | }
548 |
549 | public function pExpr_Empty(Expr\Empty_ $node) {//TODO: implement this
550 | $this->print_('empty(');
551 | $this->p($node->expr);
552 | $this->print_(')');
553 | }
554 |
555 | public function pExpr_Isset(Expr\Isset_ $node) {//TODO: implement this
556 | $this->print_('isset(');
557 | $this->pCommaSeparated($node->vars);
558 | $this->print_(')');
559 | }
560 |
561 | public function pExpr_Eval(Expr\Eval_ $node) {//TODO: implement this
562 | $this->print_('eval(');
563 | $this->p($node->expr);
564 | $this->print_(')');
565 | }
566 |
567 | /**
568 | * @param null $atStart
569 | * @return JsPrinter
570 | */
571 | public function pushDelay($atStart = null) {
572 | call_user_func_array(array($this->writer, __FUNCTION__), func_get_args());
573 | return $this;
574 | }
575 |
576 | /**
577 | * @param null $id
578 | * @return JsPrinter
579 | */
580 | public function popDelay(&$id = null) {
581 | $this->writer->popDelay($id);
582 | return $this;
583 | }
584 |
585 | /**
586 | * @param $var
587 | * @return $this
588 | */
589 | public function popDelayToVar(&$var) {
590 | $this->writer->popDelayToVar($var);
591 | return $this;
592 | }
593 |
594 | /**
595 | * @param $id
596 | * @return JsPrinter
597 | */
598 | public function writeDelay($id) {
599 | call_user_func_array(array($this->writer, __FUNCTION__), func_get_args());
600 | return $this;
601 | }
602 |
603 | /**
604 | * @return JsPrinter
605 | */
606 | public function writeLastDelay() {
607 | call_user_func_array(array($this->writer, __FUNCTION__), func_get_args());
608 | return $this;
609 | }
610 |
611 | /**
612 | * @param $string
613 | * @param ... $objects
614 | * @return JsPrinter
615 | */
616 | public function println($string = '', $objects = null) {
617 | call_user_func_array(array($this->writer, __FUNCTION__), func_get_args());
618 | return $this;
619 | }
620 |
621 | /**
622 | * @param $string
623 | * @param ... $objects
624 | * @return JsPrinter
625 | */
626 | public function print_($string, $objects = null) {
627 | call_user_func_array(array($this->writer, __FUNCTION__), func_get_args());
628 | return $this;
629 | }
630 |
631 | /**
632 | * @return JsPrinter
633 | */
634 | public function indent() {
635 | call_user_func_array(array($this->writer, __FUNCTION__), func_get_args());
636 | return $this;
637 | }
638 |
639 | /**
640 | * @return JsPrinter
641 | */
642 | public function outdent() {
643 | call_user_func_array(array($this->writer, __FUNCTION__), func_get_args());
644 | return $this;
645 | }
646 |
647 | /**
648 | * @param $string
649 | * @param ... $objects
650 | * @return JsPrinter
651 | */
652 | public function indentln($string, $objects = null) {
653 | call_user_func_array(array($this->writer, __FUNCTION__), func_get_args());
654 | return $this;
655 | }
656 | }
--------------------------------------------------------------------------------
/lib/phptojs/JsPrinter/JsPrinterInterface.php:
--------------------------------------------------------------------------------
1 | code = "";
28 | $this->codeStack = array();
29 | $this->atStartStack = array();
30 | $this->delayStack = array();
31 | $this->finalCode = "";
32 |
33 | $this->indent = 0;
34 | $this->indentStr = "";
35 | $this->atStart = true;
36 | }
37 |
38 | /**
39 | * @param null $atStart
40 | * @return $this
41 | */
42 | public function pushDelay($atStart = null) {
43 | $this->codeStack[] = $this->code;
44 | $this->atStartStack[] = $this->atStart;
45 | $this->code = "";
46 | if ($atStart !== null) {
47 | $this->atStart = $atStart;
48 | }
49 | return $this;
50 | }
51 |
52 | private $lastDelay;
53 | private $lastKey = 0;
54 |
55 | /**
56 | * @param null $id
57 | * @return $this
58 | * @throws \Exception
59 | */
60 | public function popDelay(&$id = null) {
61 | $this->delayStack[$id = $this->lastDelay = $this->lastKey++] = $this->code;
62 | $this->code = array_pop($this->codeStack);
63 | $this->atStart = array_pop($this->atStartStack);
64 | return $this;
65 | }
66 |
67 | /**
68 | * @param $var
69 | * @return $this
70 | */
71 | public function popDelayToVar(&$var) {
72 | $var = $this->code;
73 | $this->code = array_pop($this->codeStack);
74 | $this->atStart = array_pop($this->atStartStack);
75 | return $this;
76 | }
77 |
78 | /**
79 | * @param $id
80 | * @return $this
81 | */
82 | public function writeDelay($id) {
83 | $this->code .= $this->delayStack[$id];
84 | unset($this->delayStack[$id]);
85 | return $this;
86 | }
87 |
88 | /**
89 | * @return $this
90 | */
91 | public function writeLastDelay() {
92 | return $this->writeDelay($this->lastDelay);
93 | }
94 |
95 | /**
96 | * @param $string
97 | * @param ... $objects
98 | * @return $this
99 | */
100 | public function println($string = '', $objects = null) {
101 | if ($string && $objects !== null) {
102 | //$string = call_user_func_array('sprintf',func_get_args());
103 | $string = $this->printf(func_get_args());
104 | }
105 | $this->print_($string . self::$EOL);
106 | $this->atStart = true;
107 | return $this;
108 | }
109 |
110 | /**
111 | * @param $string
112 | * @param ... $objects
113 | * @return $this
114 | */
115 | public function print_($string, $objects = null) {
116 | if ($string && $objects !== null) {
117 | //$string = call_user_func_array('sprintf',func_get_args());
118 | $string = $this->printf(func_get_args());
119 | }
120 | if ($this->atStart) {
121 | $this->code .= $this->indentStr;
122 | $this->atStart = false;
123 | }
124 | $this->code .= $string;
125 | return $this;
126 | }
127 |
128 | /**
129 | * @return $this
130 | */
131 | public function indent() {
132 | $this->indentStr = str_repeat(self::$indentChr, ++$this->indent);
133 | return $this;
134 | }
135 |
136 | /**
137 | * @return $this
138 | */
139 | public function outdent() {
140 | $this->indentStr = str_repeat(self::$indentChr, --$this->indent);
141 | return $this;
142 | }
143 |
144 | /**
145 | * @param $string
146 | * @param ... $objects
147 | * @return $this
148 | */
149 | public function indentln($string, $objects = null) {
150 | if ($string && $objects !== null) {
151 | //$string = call_user_func_array('sprintf',func_get_args());
152 | $string = $this->printf(func_get_args());
153 | }
154 | $this->indent();
155 | $this->println($string);
156 | $this->outdent();
157 | return $this;
158 | }
159 |
160 | public function getCode() {
161 | if (count($this->delayStack) > 0) {
162 | throw new \Exception("DelayStack is not empty (" . count($this->delayStack) . ")");
163 | }
164 | if (count($this->codeStack) > 0) {
165 | throw new \Exception("CodeStack is not empty (" . count($this->codeStack) . ")");
166 | }
167 | return $this->finalCode . $this->code;
168 | }
169 |
170 | public function getResetCode() {
171 | $res = $this->getCode();
172 | $this->reset();
173 | return $res;
174 | }
175 |
176 | private function printf($args) {
177 | $string = array_shift($args);
178 | foreach ($args as $arg) {
179 | $string = preg_replace('/%{\w*}/', $arg, $string, 1);
180 | }
181 | return $string;
182 | }
183 |
184 | }
--------------------------------------------------------------------------------
/lib/phptojs/Printer/SourceWriterInterface.php:
--------------------------------------------------------------------------------
1 | this.___key_int) {
18 | this.___key_int = parseInt(key) + 1;
19 | }
20 | };
21 | HashArray.prototype.push = function (value) {
22 | var key = this.___key_int++;
23 | this.___keys.push(key);
24 | this[key] = value;
25 | };
26 | HashArray.prototype.delete_ = function (key) {
27 | delete this[key];
28 | this.___keys.splice(this.___keys.indexOf(item), 1);
29 | };
30 | HashArray.prototype.data = function () {
31 | return this;
32 | };
33 | HashArray.prototype.count = function () {
34 | return this.___keys.length;
35 | };
36 | HashArray.prototype.current = function () {
37 | return this[this.___keys[this.___key_pos]];
38 | };
39 | HashArray.prototype.next = function () {
40 | this.___key_pos++;
41 | };
42 | HashArray.prototype.key = function () {
43 | return this.___keys[this.___key_pos];
44 | };
45 | HashArray.prototype.valid = function () {
46 | return typeof this[this.___keys[this.___key_pos]] != 'undefined';
47 | };
48 | return HashArray;
49 | })();
50 | }
51 |
--------------------------------------------------------------------------------
/lib/phptojs/lib/js/JsArray.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by PhpStorm.
3 | * User: Jozef Môstka
4 | * Date: 4.6.2016
5 | * Time: 22:41
6 | */
7 | if (typeof N == 'undefined') N = {};
8 | if (typeof N.jsphp == 'undefined') N.jsphp = {};
9 | (function () {
10 | var functions = ["indexOf", "concat", "copyWithin", "entries", "every",
11 | "filter", "find", "findIndex", "forEach", "includes", "indexOf",
12 | "join", "keys", "lastIndexOf", "map", "pop", "push", "reduce",
13 | "reduceRight", "reverse", "shift", "slice", "some", "sort",
14 | "splice", "toLocaleString", "toSource", "toString", "unshift",
15 | "values", "fill"
16 | ];
17 |
18 | var JsArray = this.JsArray = function () {
19 | var arr = [];
20 | arr.push.apply(arr, arguments);
21 | arr.__proto__ = JsArray.prototype;
22 | if (arr.forEach === undefined) {
23 | // FIXED: for nodejs
24 | for (var i = 0; i < functions.length; i++) {
25 | arr.__proto__[functions[i]] = Array.prototype[functions[i]];
26 | }
27 | }
28 | return arr;
29 | };
30 | __extends(JsArray,Array);
31 | var arrayStaticMethods = ["from", "isArray", "of"];
32 | for (var i = 0; i < arrayStaticMethods.length; i++) {
33 | JsArray[arrayStaticMethods[i]] = Array[arrayStaticMethods[i]];
34 | }
35 | }).call(N.jsphp);
--------------------------------------------------------------------------------
/lib/phptojs/lib/js/JsObject.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by PhpStorm.
3 | * User: Jozef Môstka
4 | * Date: 4.6.2016
5 | * Time: 11:38
6 | */
7 | if (typeof N == 'undefined') N = {};
8 | if (typeof N.jsphp == 'undefined') N.jsphp = {};
9 | N.jsphp.JsObject=function () {
10 | if (arguments.length>0){
11 | if (arguments[0] instanceof Object){
12 | for(var i in arguments[0]){
13 | if (!arguments[0].hasOwnProperty(i)) continue;
14 | this[i]=arguments[0][i];
15 | }
16 | }
17 | }
18 | };
19 | N.jsphp.JsObject.prototype = new Object();
20 | N.jsphp.JsObject.assign=function (target) {
21 | if (target===null || typeof target=="undefined"){
22 | throw new Exception("Cannot convert undefined or null to object");
23 | }
24 | if (!target instanceof N.jsphp.JsObject){
25 | if (target instanceof Array || target.constructor.name=="Object") {
26 | target = new N.jsphp.JsObject(target);
27 | }else if (typeof target=="function"){
28 | return target;
29 | }else{
30 | throw new Exception("This type is not implemented yet "+(typeof target));
31 | }
32 | }
33 | if (arguments.length>1){
34 | for(var i=1;i
20 | * Return the current element
21 | * @link http://php.net/manual/en/iterator.current.php
22 | * @return mixed Can return any type.
23 | */
24 | public function current() {
25 | $this->a = &$this->data;
26 | return current($this->data);
27 | }
28 |
29 | /**
30 | * (PHP 5 >= 5.0.0)
31 | * Move forward to next element
32 | * @link http://php.net/manual/en/iterator.next.php
33 | * @return mixed next value.
34 | */
35 | public function next() {
36 | next($this->data);
37 | return $this->current();
38 | }
39 |
40 | /**
41 | * (PHP 5 >= 5.0.0)
42 | * Return the key of the current element
43 | * @link http://php.net/manual/en/iterator.key.php
44 | * @return mixed scalar on success, or null on failure.
45 | */
46 | public function key() {
47 | return key($this->data);
48 | }
49 |
50 | /**
51 | * (PHP 5 >= 5.0.0)
52 | * Checks if current position is valid
53 | * @link http://php.net/manual/en/iterator.valid.php
54 | * @return boolean The return value will be casted to boolean and then evaluated.
55 | * Returns true on success or false on failure.
56 | */
57 | public function valid() {
58 | return key($this->data) !== null;
59 | }
60 |
61 | /**
62 | * (PHP 5 >= 5.0.0)
63 | * Rewind the Iterator to the first element
64 | * @link http://php.net/manual/en/iterator.rewind.php
65 | * @return void Any returned value is ignored.
66 | */
67 | public function rewind() {
68 | reset($this->data);
69 | }
70 |
71 | /**
72 | * (PHP 5 >= 5.0.0)
73 | * Whether a offset exists
74 | * @link http://php.net/manual/en/arrayaccess.offsetexists.php
75 | * @param mixed $offset
76 | * An offset to check for.
77 | *
78 | * @return boolean true on success or false on failure.
79 | *
80 | *
81 | * The return value will be casted to boolean if non-boolean was returned.
82 | */
83 | public function offsetExists($offset) {
84 | return isset($this->data);
85 | }
86 |
87 | /**
88 | * (PHP 5 >= 5.0.0)
89 | * Offset to retrieve
90 | * @link http://php.net/manual/en/arrayaccess.offsetget.php
91 | * @param mixed $offset
92 | * The offset to retrieve.
93 | *
94 | * @return mixed Can return all value types.
95 | */
96 | public function offsetGet($offset) {
97 | return $this->data[$offset];
98 | }
99 |
100 | /**
101 | * @deprecated
102 | * @param mixed $offset
103 | * @param mixed $value
104 | * @return void
105 | */
106 | public function offsetSet($offset, $value) {
107 | throw new \BadMethodCallException("You cant set data to HashArray by \$hashArray[0]=0. use \$hasArray->set(0,0)");
108 | }
109 |
110 | /**
111 | * (PHP 5 >= 5.0.0)
112 | * Offset to unset
113 | * @link http://php.net/manual/en/arrayaccess.offsetunset.php
114 | * @param mixed $offset
115 | * The offset to unset.
116 | *
117 | * @return void
118 | */
119 | public function offsetUnset($offset) {
120 | throw new \BadMethodCallException("You cant unset data to HashArray by unset \$hashArray[0]. use \$hasArray->delete(0)");
121 | }
122 |
123 | public function set($offset, $value) {
124 | $this->data[$offset] = $value;
125 | }
126 |
127 | public function push($value) {
128 | $this->data[] = $value;
129 | }
130 |
131 | public function delete($offset) {
132 | unset($this->data[$offset]);
133 | }
134 |
135 | public function __get($name) {
136 | return $this->data[$name];
137 | }
138 |
139 | /**
140 | * (PHP 5 >= 5.1.0)
141 | * Count elements of an object
142 | * @link http://php.net/manual/en/countable.count.php
143 | * @return int The custom count as an integer.
144 | *
145 | *
146 | * The return value is cast to an integer.
147 | */
148 | public function count() {
149 | return count($this->data);
150 | }
151 |
152 | public function arr() {
153 | return $this->data;
154 | }
155 | }
--------------------------------------------------------------------------------
/lib/phptojs/lib/php/JsArray.php:
--------------------------------------------------------------------------------
1 | onlyKeys=$onlyKeys;
35 | $this->onlyValues=$onlyValues;
36 | if ($data instanceof JsArray) {
37 | $this->data = [];
38 | for ($i = 0; $i < $data->length; $i++) {
39 | if ($onlyKeys) {
40 | $this->data[] = $data[$i];
41 | } else if ($onlyValues) {
42 | $this->data[] = $data[$i];
43 | }else{
44 | $this->data[] = $data[$i];
45 | }
46 | }
47 | } else if (gettype($data) == "array" && array_values($data) === $data) {
48 | foreach ($data as $key => $value) {
49 | if ($onlyKeys) {
50 | $this->data[] = $key;
51 | } else if ($onlyValues) {
52 | $this->data[] = $value;
53 | } else {
54 | $this->data[] = $value;
55 | }
56 | }
57 | } else {
58 | throw new \Exception("Iterator can get only JsArray o indexed array");
59 | }
60 | }
61 |
62 | /**
63 | * @internal
64 | */
65 | public function current() {
66 | return current($this->data);
67 | }
68 |
69 | /**
70 | * Move forward to next element
71 | * @link http://php.net/manual/en/iterator.next.php
72 | * @return JsArrayIteratorPair Any returned value is ignored.
73 | * @since 5.0.0
74 | */
75 | public function next() {
76 | $ret = new JsArrayIteratorPair();
77 | $current = current($this->data);
78 | if ($current===false){
79 | $ret->done=true;
80 | if ($this->onlyKeys || $this->onlyValues){
81 | $ret->value=undefined;
82 | }else {
83 | $ret->value = new JsArray(
84 | undefined,
85 | undefined
86 | );
87 | }
88 | }else {
89 | $ret->done = false;
90 | if ($this->onlyKeys) {
91 | $ret->value = key($this->data);
92 | } else if ($this->onlyValues) {
93 | $ret->value = current($this->data);
94 | }else{
95 | $ret->value = new JsArray(
96 | key($this->data),
97 | current($this->data)
98 | );
99 | }
100 | next($this->data);
101 | }
102 | return $ret;
103 | }
104 |
105 | /**
106 | * @internal
107 | */
108 | public function key() {
109 | return key($this->data);
110 | }
111 |
112 | /**
113 | * @internal
114 | */
115 | public function valid() {
116 | return key($this->data)!==null;
117 | }
118 |
119 | /**
120 | * @internal
121 | */
122 | public function rewind() {
123 | // reset($this->data);
124 | }
125 | }
126 |
127 | namespace jsphp;
128 | use jsphp\util\JsArrayIterator;
129 |
130 | /**
131 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
132 | * The JavaScript Array object is a global object that is used in the construction of arrays; which are high-level, list-like objects.
133 | * @property int length
134 | * @package jsphp
135 | */
136 | class JsArray implements \ArrayAccess, \JsonSerializable{
137 |
138 | private $data;
139 |
140 | /**
141 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
142 | * The JavaScript Array object is a global object that is used in the construction of arrays; which are high-level, list-like objects.
143 | * @param int|mixed $length If the only argument passed to the Array constructor is an integer between 0 and 232-1 (inclusive), this returns a new JavaScript array with length set to that number. If the argument is any other number, a RangeError exception is thrown.
144 | * @param mixed [$elementN] A JavaScript array is initialized with the given elements, except in the case where a single argument is passed to the Array constructor and that argument is a number (see the arrayLength parameter below).Note that this special case only applies to JavaScript arrays created with the Array constructor, not array literals created with the bracket syntax.
145 | */
146 | public function __construct() {
147 | $args=func_get_args();
148 | if (count($args)==1 && is_numeric($args[0])){
149 | while($args[0]-->0){
150 | $this->data[]=undefined;
151 | }
152 | }else if(count($args)>0){
153 | foreach($args as $arg){
154 | $this->data[]=$arg;
155 | }
156 | }
157 | }
158 |
159 | /**
160 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
161 | * Calls a defined callback function on each element of an array, and returns an array that contains the results.
162 | * @param callback $callback A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
163 | */
164 | public function forEach($callback){
165 |
166 | foreach ($this->data as $index=>$value){
167 | call_user_func_array($callback,[$value,$index,$this]);
168 | }
169 | }
170 |
171 | /**
172 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
173 | * The Array::from() method creates a new Array instance from an array-like or iterable object.
174 | * @param array|JsArray|string|JsObject $arrayLike An array-like or iterable object to convert to an array.
175 | * @param callable $mapFunction Optional. Map function to call on every element of the array.
176 | * @return JsArray
177 | * @throws \Exception
178 | */
179 | public static function from($arrayLike, $mapFunction=null){
180 | $ret = new JsArray();
181 | if ($mapFunction!==null){
182 | if (gettype($arrayLike)=="array" && array_key_exists("length",$arrayLike)) {
183 | for($i=0;$i<$arrayLike["length"];$i++){
184 | $ret->data[] = call_user_func_array($mapFunction, [undefined,$i]);
185 | }
186 | return $ret;
187 | }
188 | if($arrayLike instanceof JsObject && $arrayLike->offsetExists("length")){
189 | for($i=0;$i<$arrayLike["length"];$i++){
190 | $ret->data[] = call_user_func_array($mapFunction, [undefined,$i]);
191 | }
192 | return $ret;
193 | }
194 | if($arrayLike instanceof JsArray){
195 | foreach ($arrayLike->data as $key=>$val) {
196 | $ret->data[] = call_user_func_array($mapFunction, [$val,$key]);
197 | }
198 | return $ret;
199 | }
200 | if(is_string($arrayLike)) {
201 | $chars = str_split($arrayLike);
202 | foreach ($chars as $char) {
203 | $ret->data[] = $char;
204 | }
205 | return $ret;
206 | }
207 | foreach ($arrayLike as $key=>$val) {
208 | $ret->data[] = call_user_func_array($mapFunction, [$val,$key]);
209 | }
210 | return $ret;
211 | }else{
212 | if (gettype($arrayLike)=="array"){
213 | if (array_values($arrayLike) === $arrayLike){
214 | foreach ($arrayLike as $val){
215 | $ret->data[]=$val;
216 | }
217 | return $ret;
218 | }
219 | if (array_key_exists("length",$arrayLike)) {
220 | for($i=0;$i<$arrayLike["length"];$i++){
221 | $ret->data[] = call_user_func_array($mapFunction, [undefined,$i]);
222 | }
223 | return $ret;
224 | }
225 | return $ret;
226 | }
227 | if($arrayLike instanceof JsObject && $arrayLike->offsetExists("length")){
228 | for($i=0;$i<$arrayLike["length"];$i++){
229 | $ret->data[] = call_user_func_array($mapFunction, [undefined,$i]);
230 | }
231 | return $ret;
232 | }
233 | if ($arrayLike instanceof JsArray){
234 | foreach ($arrayLike->data as $val){
235 | $ret->data[]=$val;
236 | }
237 | return $ret;
238 | }
239 | if(is_string($arrayLike)){
240 | $chars = str_split($arrayLike);
241 | foreach($chars as $char){
242 | $ret->data[]=$char;
243 | }
244 | return $ret;
245 | }
246 | if ($arrayLike===null || $arrayLike===undefined){
247 | throw new \Exception("Cannot convert undefined or null to object");
248 | }
249 | return $ret;
250 | }
251 | }
252 |
253 | /**
254 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
255 | * The Array::isArray() determines whether the passed value is an Array.
256 | * @param mixed $object The object to be checked.
257 | * @return bool If the object is an Array, true is returned, otherwise false is.
258 | */
259 | public static function isArray($object){
260 | if (gettype($object)=="array" && array_values($object) === $object){
261 | return true;
262 | }
263 | if ($object instanceof JsArray){
264 | return true;
265 | }
266 | return false;
267 | }
268 |
269 | /**
270 | * The Array.of() method creates a new Array instance with a variable number of arguments, regardless of number or type of the arguments.
271 | *
272 | * The difference between Array.of() and the Array constructor is in the handling of integer arguments: Array.of(42) creates an array with a single element, 42, whereas Array(42) creates an array with 42 elements, each of which is undefined.
273 | * @param mixed ...element Elements of which to create the array.
274 | * @return JsArray
275 | */
276 | public static function of($element){
277 | $ret = new JsArray();
278 | foreach (func_get_args() as $val){
279 | $ret->data[]=$val;
280 | }
281 | return $ret;
282 | }
283 |
284 | /**
285 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of
286 | * The concat() method returns a new array comprised of the array on which it is called joined with the array(s) and/or value(s) provided as arguments.
287 | * @param mixed|array ...$valueN Arrays and/or values to concatenate into a new array. See the description below for details.
288 | * @return JsArray
289 | */
290 | public function concat($valueN){
291 | $ret = new JsArray();
292 | foreach ($this->data as $item) {
293 | $ret->data[]=$item;
294 | }
295 | $args=func_get_args();
296 | foreach($args as $arg){
297 | if (gettype($arg)=="array" && array_values($arg) === $arg){
298 | foreach ($arg as $val){
299 | $ret->data[]=$val;
300 | }
301 | }else if ($arg instanceof JsArray){
302 | foreach ($arg->data as $value) {
303 | $ret->data[]=$value;
304 | }
305 | }else{
306 | $ret->data[]=$arg;
307 | }
308 | }
309 | return $ret;
310 | }
311 |
312 | /**
313 | * The entries() method returns a new Array Iterator object that contains the key/value pairs for each index in the array.
314 | */
315 | public function entries(){
316 | return new JsArrayIterator($this);
317 | }
318 |
319 | /**
320 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries
321 | * The every() method tests whether all elements in the array pass the test implemented by the provided function.
322 | * @param callable $callback Function to test for each element.
323 | * @return bool
324 | * @throws \Exception
325 | */
326 | public function every($callback){
327 | if (!is_callable($callback)){
328 | throw new \Exception("this is not a function");
329 | }
330 | foreach($this->data as $key=>$value){
331 | if (!call_user_func_array($callback,[$value,$key,$this])){
332 | return false;
333 | }
334 | }
335 | return true;
336 | }
337 |
338 | /**
339 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
340 | * The fill() method fills all the elements of an array from a start index to an end index with a static value.
341 | * @param mixed $value
342 | * @param int $start
343 | * @param int $end
344 | * @return JsArray
345 | */
346 | public function fill($value,$start=null,$end=null){
347 | $len = count($this->data);
348 | if ($start==null){
349 | $start=0;
350 | }
351 | if ($end==null){
352 | $end=$len;
353 | }
354 | if (((int)$start)!==$start || ((int)$end)!==$end){
355 | return $this;
356 | }
357 | $start = $start < 0 ?
358 | max($len + $start, 0) :
359 | min($start, $len);
360 | $end = $end < 0 ?
361 | max($len + $end, 0) :
362 | min($end, $len);
363 | for($i=$start;$i<$end;$i++){
364 | $this->data[$i]=$value;
365 | }
366 | return $this;
367 | }
368 |
369 | /**
370 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
371 | * The filter() method creates a new array with all elements that pass the test implemented by the provided function.
372 | * @param callable $callback Function to test each element of the array. Invoked with arguments (element, index, array). Return true to keep the element, false otherwise.
373 | * @return JsArray
374 | * @throws \Exception
375 | */
376 | public function filter($callback){
377 | if (!is_callable($callback)){
378 | throw new \Exception("this is not a function");
379 | }
380 | $ret = new JsArray();
381 | foreach($this->data as $key=>$value){
382 | if (call_user_func_array($callback,[$value,$key,$this])){
383 | $ret->data[]=$value;
384 | }
385 | }
386 | return $ret;
387 | }
388 |
389 | /**
390 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
391 | * The find() method returns a value in the array, if an element in the array satisfies the provided testing function. Otherwise undefined is returned.
392 | * @param callable $callback
393 | * @throws \Exception
394 | */
395 | public function find($callback){
396 | if (!is_callable($callback)){
397 | throw new \Exception("this is not a function");
398 | }
399 | foreach($this->data as $key=>$value){
400 | if (call_user_func_array($callback,[$value,$key,$this])){
401 | return $value;
402 | }
403 | }
404 | return undefined;
405 | }
406 |
407 | /**
408 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
409 | * The findIndex() method returns an index in the array, if an element in the array satisfies the provided testing function. Otherwise -1 is returned.
410 | * @param callable $callback
411 | * @return int
412 | * @throws \Exception
413 | */
414 | public function findIndex($callback){
415 | if (!is_callable($callback)){
416 | throw new \Exception("this is not a function");
417 | }
418 | foreach($this->data as $key=>$value){
419 | if (call_user_func_array($callback,[$value,$key,$this])){
420 | return $key;
421 | }
422 | }
423 | return -1;
424 | }
425 |
426 | /**
427 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
428 | * The includes() method determines whether an array includes a certain element, returning true or false as appropriate.
429 | * @param mixed $searchElement The element to search for.
430 | * @param int $fromIndex Optional. The position in this array at which to begin searching for searchElement. A negative value searches from the index of array.length + fromIndex by asc. Defaults to 0.
431 | * @return bool
432 | */
433 | public function includes($searchElement,$fromIndex=0){
434 | $len=count($this->data);
435 | if ($len==0){
436 | return false;
437 | }
438 | if ($fromIndex < 0) {
439 | $fromIndex = $len + $fromIndex;
440 | if ($fromIndex < 0) {
441 | $fromIndex = 0;
442 | }
443 | }
444 | while ($fromIndex < $len) {
445 | $currentElement = $this->data[$fromIndex];
446 | if ($searchElement === $currentElement) {
447 | return true;
448 | }
449 | $fromIndex++;
450 | }
451 | return false;
452 | }
453 |
454 | /**
455 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
456 | * The indexOf() method returns the first index at which a given element can be found in the array, or -1 if it is not present.
457 | * @param mixed $searchElement Element to locate in the array.
458 | * @param int $fromIndex The index to start the search at. If the index is greater than or equal to the array's length, -1 is returned, which means the array will not be searched. If the provided index value is a negative number, it is taken as the offset from the end of the array. Note: if the provided index is negative, the array is still searched from front to back. If the calculated index is less than 0, then the whole array will be searched. Default: 0 (entire array is searched).
459 | * @return int
460 | */
461 | public function indexOf($searchElement,$fromIndex=0){
462 | $len = count($this->data);
463 | if ($len==0){
464 | return -1;
465 | }
466 | if ($fromIndex >= $len) {
467 | return -1;
468 | }
469 | $fromIndex = max($fromIndex >= 0 ? $fromIndex : $len - abs($fromIndex), 0);
470 | while ($fromIndex < $len) {
471 | if ($this->data[$fromIndex] === $searchElement) {
472 | return $fromIndex;
473 | }
474 | $fromIndex++;
475 | }
476 | return -1;
477 | }
478 |
479 | /**
480 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join
481 | * The join() method joins all elements of an array into a string.
482 | * @param string $separator
483 | * @return string
484 | */
485 | public function join($separator=","){
486 | return join($separator, $this->data);
487 | }
488 |
489 | /**
490 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/keys
491 | * The keys() method returns a new Array Iterator that contains the keys for each index in the array.
492 | * @return JsArrayIterator
493 | */
494 | public function keys(){
495 | return new JsArrayIterator($this,true);
496 | }
497 |
498 | /**
499 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf
500 | * The lastIndexOf() method returns the last index at which a given element can be found in the array, or -1 if it is not present. The array is searched backwards, starting at fromIndex.
501 | * @param mixed $searchElement Element to locate in the array.
502 | * @param int $fromIndex Optional. The index at which to start searching backwards. Defaults to the array's length minus one, i.e. the whole array will be searched. If the index is greater than or equal to the length of the array, the whole array will be searched. If negative, it is taken as the offset from the end of the array. Note that even when the index is negative, the array is still searched from back to front. If the calculated index is less than 0, -1 is returned, i.e. the array will not be searched.
503 | * @return int
504 | */
505 | public function lastIndexOf($searchElement,$fromIndex=null){
506 | $len=count($this->data);
507 | if ($len === 0) {
508 | return -1;
509 | }
510 | if ($fromIndex===null){
511 | $fromIndex=$len-1;
512 | }else{
513 | if (((int)$fromIndex)!==$fromIndex){
514 | $fromIndex=0;
515 | }
516 | }
517 |
518 | for ($k = $fromIndex >= 0 ? min($fromIndex, $len - 1) : $len - abs($fromIndex); $k >= 0; $k--) {
519 | if ($this->data[$k] === $searchElement) {
520 | return $k;
521 | }
522 | }
523 | return -1;
524 | }
525 |
526 | /**
527 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
528 | * The map() method creates a new array with the results of calling a provided function on every element in this array.
529 | * @param callable $callback Function that produces an element of the new Array
530 | * @return JsArray
531 | * @throws \Exception
532 | */
533 | public function map($callback){
534 | if (!is_callable($callback)){
535 | throw new \Exception("this is not a function");
536 | }
537 | $ret = new JsArray();
538 | foreach ($this->data as $index=>$value) {
539 | $ret->data[$index]=call_user_func_array($callback,[$value,$index,$this]);
540 | }
541 | return $ret;
542 | }
543 |
544 | /**
545 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop
546 | * The pop() method removes the last element from an array and returns that element.
547 | */
548 | public function pop(){
549 | return array_pop($this->data);
550 | }
551 |
552 | /**
553 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push
554 | * The push() method adds one or more elements to the end of an array and returns the new length of the array.
555 | * @param mixed ...$value
556 | * @return int
557 | */
558 | public function push($value){
559 | foreach (func_get_args() as $arg) {
560 | array_push($this->data,$arg);
561 | }
562 | return count($this->data);
563 | }
564 |
565 | /**
566 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
567 | * The reduce() method applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value.
568 | * @param callable $callback
Function to execute on each value in the array, taking four arguments:
569 | *
570 | *
previousValue
571 | * The value previously returned in the last invocation of the callback, or initialValue, if supplied. (See below.)
572 | *
currentValue
573 | * The current element being processed in the array.
574 | *
currentIndex
575 | * The index of the current element being processed in the array.
576 | *
array
577 | *
578 | * The array reduce was called upon.
579 | * @param int $initialValue
580 | * @return int
581 | * @throws \Exception
582 | */
583 | public function reduce($callback, $initialValue=0){
584 | if (!is_callable($callback)){
585 | throw new \Exception("this is not a function");
586 | }
587 | foreach ($this->data as $index=>$value) {
588 | $initialValue = call_user_func_array($callback,[$initialValue,$value,$index,$this]);
589 | }
590 | return $initialValue;
591 | }
592 |
593 | /**
594 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight
595 | * The reduceRight() method applies a function against an accumulator and each value of the array (from right-to-left) has to reduce it to a single value.
596 | * @param callable $callback
Function to execute on each value in the array, taking four arguments:
597 | *
598 | *
previousValue
599 | * The value previously returned in the last invocation of the callback, or initialValue, if supplied. (See below.)
600 | *
currentValue
601 | * The current element being processed in the array.
602 | *
currentIndex
603 | * The index of the current element being processed in the array.
604 | *
array The array reduce was called upon.
605 | *
606 | * @param int $initialValue
607 | * @return int
608 | * @throws \Exception
609 | */
610 | public function reduceRight($callback, $initialValue=0){
611 | if (!is_callable($callback)){
612 | throw new \Exception("this is not a function");
613 | }
614 | for ($i=count($this->data)-1;$i>0;$i--) {
615 | $initialValue = call_user_func_array($callback,[$initialValue, $this->data[$i],$i,$this]);
616 | }
617 | return $initialValue;
618 | }
619 |
620 | /**
621 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse
622 | * The reverse() method reverses an array in place. The first array element becomes the last and the last becomes the first.
623 | * @return JsArray
624 | */
625 | public function reverse(){
626 | $this->data = array_reverse($this->data,false);
627 | return $this;
628 | }
629 |
630 | /**
631 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
632 | * The shift() method removes the first element from an array and returns that element. This method changes the length of the array.
633 | */
634 | public function shift(){
635 | return array_shift($this->data);
636 | }
637 |
638 | /**
639 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
640 | * The slice() method returns a shallow copy of a portion of an array into a new array object.
641 | * @param int $begin
Zero-based index at which to begin extraction.
642 | * As a negative index, begin indicates an offset from the end of the sequence. slice(-2) extracts the last two elements in the sequence.
643 | * If begin is undefined, slice begins from index 0.
644 | * @param int $end
Zero-based index at which to end extraction. slice extracts up to but not including end.
645 | * slice(1,4) extracts the second element through the fourth element (elements indexed 1, 2, and 3).
646 | * As a negative index, end indicates an offset from the end of the sequence. slice(2,-1) extracts the third element through the second-to-last element in the sequence.
647 | * If end is omitted, slice extracts through the end of the sequence (arr.length).
648 | * @return JsArray
649 | */
650 | public function slice($begin = 0, $end = null){
651 | if ($end===null){
652 | $end=count($this->data)-1;
653 | }
654 | $ret = new JsArray();
655 | $ret->data = array_slice($this->data,$begin,$begin-$end,false);
656 | return $ret;
657 | }
658 |
659 | /**
660 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
661 | * The some() method tests whether some element in the array passes the test implemented by the provided function.
662 | * @param callable $callback
Function to test for each element, taking three arguments:
663 | *
664 | *
currentValue
665 | * The current element being processed in the array.
666 | *
index
667 | * The index of the current element being processed in the array.
668 | *
array The array reduce was called upon.
669 | *
670 | * @return bool
671 | * @throws \Exception
672 | */
673 | public function some($callback){
674 | if (!is_callable($callback)){
675 | throw new \Exception("this is not a function");
676 | }
677 | foreach ($this->data as $index=>$value) {
678 | if (call_user_func_array($callback,[$value,$index,$this])==true){
679 | return true;
680 | }
681 | }
682 | return false;
683 | }
684 |
685 | /**
686 | * The sort() method sorts the elements of an array in place and returns the array. The sort is not necessarily stable. The default sort order is according to string Unicode code points.
687 | * @param callable $callback Optional. Specifies a function that defines the sort order. If omitted, the array is sorted according to each character's Unicode code point value, according to the string conversion of each element.
688 | * @return $this
689 | * @throws \Exception
690 | */
691 | public function sort($callback=null){
692 | if ($callback!=null && !is_callable($callback)){
693 | throw new \Exception("this is not a function");
694 | }
695 | if ($callback!==null){
696 | usort($this->data,$callback);
697 | }else{
698 | usort($this->data,"strcoll");
699 | }
700 | return $this;
701 | }
702 |
703 | /**
704 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
705 | * The splice() method changes the content of an array by removing existing elements and/or adding new elements.
706 | * @param int $start Index at which to start changing the array (with origin 0). If greater than the length of the array, actual starting index will be set to the length of the array. If negative, will begin that many elements from the end.
707 | * @param int $deleteCount
An integer indicating the number of old array elements to remove. If deleteCount is 0, no elements are removed. In this case, you should specify at least one new element. If deleteCount is greater than the number of elements left in the array starting at start, then all of the elements through the end of the array will be deleted.
708 | * If deleteCount is omitted, deleteCount will be equal to (arr.length - start).
709 | * @param mixed [...$replacement] The elements to add to the array, beginning at the start index. If you don't specify any elements, splice() will only remove elements from the array.
710 | * @return JsArray
711 | */
712 | public function splice($start,$deleteCount=null){
713 | if ($deleteCount===null){
714 | $deleteCount=count($this->data)-1;
715 | }
716 | $len = $deleteCount;
717 | $ret = new JsArray();
718 |
719 | $args = func_get_args();
720 | if (count($args)==3) {
721 | $ret->data = array_splice($this->data, $start, $len,$args[2]);
722 | }else if (count($args)>3){
723 | $replacement = [];
724 | for($i=2;$idata = array_splice($this->data, $start, $len,$replacement);
728 | }else{
729 | $ret->data = array_splice($this->data, $start, $len);
730 | }
731 | return $ret;
732 | }
733 |
734 | /**
735 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift
736 | * The unshift() method adds one or more elements to the beginning of an array and returns the new length of the array.
737 | * @param mixed [...$element] The elements to add to the front of the array.
738 | * @return int The new length property of the object upon which the method was called.
739 | */
740 | public function unshift(){
741 | $args = func_get_args();
742 | for ($i=count($args)-1;$i>=0;$i--){
743 | array_unshift($this->data,$args[$i]);
744 | }
745 | return count($this->data);
746 | }
747 |
748 | /**
749 | * @param mixed $offset
750 | * @internal
751 | * @return bool
752 | */
753 | public function offsetExists($offset) {
754 | return isset($this->data[$offset]);
755 | }
756 |
757 | /**
758 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values
759 | * The values() method returns a new Array Iterator object that contains the values for each index in the array.
760 | */
761 | public function values(){
762 | return new JsArrayIterator($this,false,true);
763 | }
764 |
765 | /**
766 | * @param mixed $offset
767 | * @internal
768 | * @return mixed
769 | */
770 | public function offsetGet($offset) {
771 | return $this->data[$offset];
772 | }
773 |
774 | /**
775 | * @param mixed $offset
776 | * @param mixed $value
777 | * @internal
778 | * @return mixed
779 | */
780 | public function offsetSet($offset, $value) {
781 | if (((int)$offset)!=$offset){
782 | return $value;
783 | }
784 | if (count($this->data)>$offset){
785 | while($offset++data)){
786 | $this->data[]=undefined;
787 | }
788 | }
789 | $this->data[$offset]=$value;
790 | return $value;
791 | }
792 |
793 | /**
794 | * @param mixed $offset
795 | * @internal
796 | */
797 | public function offsetUnset($offset) {
798 | unset($this->data[$offset]);
799 | }
800 |
801 | /**
802 | * @return mixed
803 | * @internal
804 | */
805 | function jsonSerialize() {
806 | return $this->data;
807 | }
808 |
809 | /**
810 | * @param $name
811 | * @internal
812 | * @return int|null
813 | */
814 | public function __get($name) {
815 | switch ($name){
816 | case "length": return count($this->data);
817 | }
818 |
819 | return null;
820 | }
821 | }
822 |
--------------------------------------------------------------------------------
/lib/phptojs/lib/php/JsObject.php:
--------------------------------------------------------------------------------
1 | data=$arg;
28 | $this->sortData();
29 | }else if ($arg instanceof JsObject) {
30 | $this->data=$arg->data;
31 | $this->sortData();
32 | }else{
33 | throw new \Exception("Constructor on JsObject can get only array as parameter");
34 | }
35 | }
36 | }
37 |
38 | public static function freeze(JsObject $target){
39 | $target->isFreeze=true;
40 | return $target;
41 | }
42 |
43 | public static function isExtensible(JsObject $target){
44 | return $target->isExtensible;
45 | }
46 |
47 | /**
48 | * @param array|JsObject $target
49 | * @return array
50 | * @throws \Exception
51 | */
52 | public static function getOwnPropertyNames($target){
53 | if ($target instanceof JsObject){
54 | return array_keys($target->data);
55 | }
56 | if (is_array($target)){
57 | return array_keys($target);
58 | }
59 | if (is_string($target)){
60 | $chars = str_split($target);
61 | $chars[]="length";
62 | return $chars;
63 | }
64 | throw new \Exception("Cannot convert target or null to object");
65 | }
66 |
67 | /**
68 | * @param ...$target
69 | * @return JsObject
70 | * @throws \Exception
71 | */
72 | public static function assign($target){
73 | $args = func_get_args();
74 | if (gettype($target)=="array"){
75 | $target = new JsObject($target);
76 | }elseif ($target instanceof JsObject){
77 | if ($target->isFreeze || !$target->isExtensible){
78 | throw new \Exception("Can't add property, object is not extensible");
79 | }
80 | }else if(gettype($target)=="object"){
81 | }else if(get_class($target)=="Closure"){
82 | return $target;
83 | }else{
84 | throw new \Exception("This type is not implemented yet ".gettype($target));
85 | }
86 | if (count($args)>1){
87 | for($i=1;$i$char){
90 | $target->data[$pos]=$char;
91 | }
92 | }elseif(is_array($args[$i]) || $args[$i] instanceof JsObject) {
93 | foreach ($args[$i] as $key => $val) {
94 | $target->data[$key] = $val;
95 | }
96 | }elseif (is_null($args[$i]) || $args[$i]===undefined || is_bool($args[$i]) || is_numeric($args[$i])) {
97 | continue;
98 | }else{
99 | throw new \Exception("This type is not implemented yet ".gettype($args[$i]));
100 | }
101 | }
102 | }
103 | $target->sortData();
104 | return $target;
105 | }
106 |
107 | private function sortData(){
108 | uksort($this->data,function($a, $b){
109 | if (gettype($a)=="string" && gettype($b)=="string"){
110 | return 0;
111 | }
112 | if (gettype($a)=="integer" && gettype($b)=="string"){
113 | return 0;
114 | }
115 | if (gettype($a)=="string" && gettype($b)=="integer"){
116 | return 1;
117 | }
118 | if (gettype($a)=="integer" && gettype($b)=="integer"){
119 | return $a>$b;
120 | }
121 | throw new \Exception("Unimplemented sort types ".gettype($a)."-".gettype($b));
122 | });
123 | }
124 |
125 | /**
126 | * Return the current element
127 | * @link http://php.net/manual/en/iterator.current.php
128 | * @return mixed Can return any type.
129 | * @since 5.0.0
130 | */
131 | public function current() {
132 | return current($this->data);
133 | }
134 |
135 | /**
136 | * Move forward to next element
137 | * @link http://php.net/manual/en/iterator.next.php
138 | * @return void Any returned value is ignored.
139 | * @since 5.0.0
140 | */
141 | public function next() {
142 | next($this->data);
143 | }
144 |
145 | /**
146 | * Return the key of the current element
147 | * @link http://php.net/manual/en/iterator.key.php
148 | * @return mixed scalar on success, or null on failure.
149 | * @since 5.0.0
150 | */
151 | public function key() {
152 | return key($this->data);
153 | }
154 |
155 | /**
156 | * Checks if current position is valid
157 | * @link http://php.net/manual/en/iterator.valid.php
158 | * @return boolean The return value will be casted to boolean and then evaluated.
159 | * Returns true on success or false on failure.
160 | * @since 5.0.0
161 | */
162 | public function valid() {
163 | return key($this->data)!==null;
164 | }
165 |
166 | /**
167 | * Rewind the Iterator to the first element
168 | * @link http://php.net/manual/en/iterator.rewind.php
169 | * @return void Any returned value is ignored.
170 | * @since 5.0.0
171 | */
172 | public function rewind() {
173 | reset($this->data);
174 | }
175 |
176 | /**
177 | * Whether a offset exists
178 | * @link http://php.net/manual/en/arrayaccess.offsetexists.php
179 | * @param mixed $offset
180 | * An offset to check for.
181 | *
182 | * @return boolean true on success or false on failure.
183 | *
184 | *
185 | * The return value will be casted to boolean if non-boolean was returned.
186 | * @since 5.0.0
187 | */
188 | public function offsetExists($offset) {
189 | return isset($this->data[$offset]);
190 | }
191 |
192 | /**
193 | * Offset to retrieve
194 | * @link http://php.net/manual/en/arrayaccess.offsetget.php
195 | * @param mixed $offset
196 | * The offset to retrieve.
197 | *
198 | * @return mixed Can return all value types.
199 | * @since 5.0.0
200 | */
201 | public function offsetGet($offset) {
202 | return $this->data[$offset];
203 | }
204 |
205 | /**
206 | * Offset to set
207 | * @link http://php.net/manual/en/arrayaccess.offsetset.php
208 | * @param mixed $offset
209 | * The offset to assign the value to.
210 | *