├── .gitignore ├── README.md ├── composer.json ├── license.txt ├── php.js ├── src └── PHPToJavascript │ ├── ArrayScope.php │ ├── CatchScope.php │ ├── ClassScope.php │ ├── CodeConverterState.php │ ├── CodeConverterState │ ├── ARRAY.php │ ├── CLASS.php │ ├── CLOSEPARENS.php │ ├── CapturingDefaultValue.php │ ├── Comma.php │ ├── Default.php │ ├── DoubleArrow.php │ ├── Echo.php │ ├── EmbeddedVariable.php │ ├── EndOfClass.php │ ├── Equals.php │ ├── FUNCTION.php │ ├── ImportNamespace.php │ ├── REQUIRE.php │ ├── SkipToSemiColon.php │ ├── TABSTRACT.php │ ├── TABSTRACTREMOVE.php │ ├── TCATCH.php │ ├── TDOUBLECOLON.php │ ├── TEXTENDS.php │ ├── TFOREACH.php │ ├── TGLOBAL.php │ ├── TIMPLEMENTSINTERFACE.php │ ├── TINTERFACE.php │ ├── TNAMESPACE.php │ ├── TNEW.php │ ├── TOBJECTOPERATOR.php │ ├── TPRIVATE.php │ ├── TPUBLIC.php │ ├── TSTATIC.php │ ├── TSTRING.php │ ├── TTRY.php │ ├── TUNSET.php │ ├── TUSE.php │ ├── TVARIABLE.php │ ├── TVARIABLEARRAY.php │ ├── TVARIABLECATCH.php │ ├── TVARIABLECLASS.php │ ├── TVARIABLEFUNCTION.php │ ├── TVARIABLEGLOBAL.php │ ├── TVARIABLEPARAMETER.php │ ├── VariableDefault.php │ ├── VariableValue.php │ └── define.php │ ├── CodeScope.php │ ├── ConverterStateMachine.php │ ├── FunctionParameterScope.php │ ├── FunctionScope.php │ ├── GlobalScope.php │ ├── PHPToJavascript.php │ ├── TokenStream.php │ └── Variable.php └── tests ├── ArrayExample.php ├── AssigningThis.php ├── BugReports.php ├── ClassExample.php ├── ClassSetVarExample.php ├── Closure.php ├── CustomEvent.php ├── DefaultValue.php ├── Inheritance.php ├── MultiLine.php ├── NameSpace.php ├── PublicPrivate.php ├── SimpleExample.php ├── SimpleOps.php ├── SplClassLoader.php ├── StaticAccessor.php ├── StaticTest.php ├── SwitchStatement.php ├── Template.php ├── Ternary.php ├── Ternary2.php ├── TraitExample.php ├── TraitInclude.php ├── TryCatch.php ├── TypeHinting.php ├── UnsetTest.php ├── continue.php ├── countItems.php ├── examples.php ├── jquery-1.9.1.min.js ├── output └── doNotDelete.txt ├── php.js └── testStart.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | export 3 | 4 | .idea 5 | .DS_Store 6 | 7 | tests/output/*.js 8 | tests/output/*.html 9 | 10 | /.gitignore.swp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This was a joke project that went far too far. I'm archiving it....but leaving it online as a piece of art. 2 | 3 | PHP-to-Javascript 4 | ================= 5 | 6 | A tool for converting simple PHP objects to Javascript code, so that code for manipulating objects can be used both server-side and client-side. 7 | 8 | There is a page with a set of example code that can be [converted online here] (http://www.basereality.com/PHPToJavascript/) "PHP to Javascript conversion"). 9 | 10 | This is not meant to be use to convert arbitrary PHP code to Javascript as that is not possible due to differences between the two languages. It is meant to be used to write explicitly simple PHP code that can also be compiled to Javascript, rather than converting vast swathes of PHP to Javascript. 11 | 12 | How to use 13 | ========== 14 | 15 | Examples 16 | -------- 17 | 18 | cd tests 19 | 20 | php examples.php 21 | 22 | This will convert the PHP files in the tests directory and for each write a javascript file equivalent. 23 | 24 | 25 | Programmatically 26 | ---------------- 27 | 28 | * Install Composer from http://getcomposer.org/ 29 | 30 | * Add the "base-reality/php-to-javascript": ">=0.0.3" to your project's composer.json file: 31 | 32 | "require":{ 33 | "base-reality/php-to-javascript": "0.1.16" 34 | } 35 | 36 | Or the latest tagged version. The dev master should only be used for development, not production. 37 | 38 | * Include the Composer SPL autoload file in your project: 39 | 40 | require_once('../vendor/autoload.php'); 41 | 42 | 43 | * Call the converter: 44 | 45 | $phpToJavascript = new PHPToJavascript\PHPToJavascript(); 46 | $phpToJavascript->addFromFile($inputFilename); 47 | $jsOutput = $phpToJavascript->toJavascript(); 48 | 49 | $jsOutput will now contain an auto-generated Javascript version of the PHP source file. 50 | 51 | 52 | TODO 53 | ==== 54 | 55 | * Setup some automated testing. 56 | 57 | * Add hasOwnProperty check to foreach loops. 58 | 59 | * Add support for const to be same as public static. 60 | 61 | * Figure out what to do about Javascript reserved keywords. Probably out to detect them and either warn or give an error on detection. testObject.delete(); 62 | 63 | * Support arrays for variables in class declaration. 64 | 65 | * Add conversion of PHP array push to Javascript array push 66 | PHP => $withoutTags[] = false; 67 | JS => withoutTags.push(false); 68 | 69 | * Add support for array with single element false; 70 | PHP => $withoutTags = array(false); 71 | JS => var withoutTags = {false}; 72 | 73 | * Statis class variables should be in class scope not the global one. 74 | SM PHPToJavascript\CodeConverterState_TSTRING token [T_STRING] => [ClassExample] 75 | SM PHPToJavascript\CodeConverterState_Default token [T_DOUBLE_COLON] => [::] 76 | SM PHPToJavascript\CodeConverterState_Default token [T_VARIABLE] => [$testStatic] 77 | SM PHPToJavascript\CodeConverterState_TVARIABLE token [T_VARIABLE] => [$testStatic] 78 | SM PHPToJavascript\CodeConverterState_TVARIABLEGLOBAL token [T_VARIABLE] => [$testStatic] 79 | Added variable testStatic to scope PHPToJavascript\GlobalScope 80 | 81 | 82 | Limitations 83 | =========== 84 | 85 | There are several features of the PHP language that either are too difficult to map into Javascript, or are just not possible in Javascript. These features will almost certainly never be implemented (at least by myself) so if you're waiting for these to be done, give up early. 86 | 87 | Pass scalars by reference 88 | ------------------------- 89 | 90 | PHP allows you to pass scalar values by reference into a function. This feature does not exist in Javascript and so cannot be supported without a huge amount of effort. 91 | 92 | Arrays passed by copy in PHP, by reference in Javascript 93 | -------------------------------------------------------- 94 | 95 | In PHP arrays are passed by copying the array into a function. In the converted Javascript, arrays are converted to objects and objects are passed by reference, so any modification to the parameter also modified the variable in the original scope - [see](https://github.com/Danack/PHP-to-Javascript/issues/56). 96 | 97 | 98 | Static class variables are always public 99 | ---------------------------------------- 100 | 101 | Due to the way objects in Javascript are implemented static class variables will always have public scope. 102 | 103 | 104 | Defines are converted to value, but not defined in Javascript. 105 | ------------------------------------------------------------- 106 | 107 | The code: 108 | 109 | define('DATABASE_TYPE', 'MySQL'); 110 | echo "Database type is ".DATABASE_TYPE; 111 | 112 | is converted to: 113 | 114 | // define('DATABASE_TYPE', 'MySQL'); 115 | document.write( "Database type is " + 'MySQL'); 116 | 117 | If this is a problem for you - current solution is don't use defines. Instead use classes to define your const variables. It would be possible to add the define as a variable in the Global scope for Javascript. But that would be kind of sucky. 118 | 119 | 120 | Associative arrays aren't ordered in Javascript. 121 | ------------------------------------------------ 122 | 123 | In PHP an array will have the same order it's declared in e.g. 124 | 125 | $testArray = array( 126 | 'salutation' => 'Hello', 127 | ' ', 128 | 'people' => 'world' 129 | ); 130 | 131 | foreach($testArray as $string){ 132 | echo $string; 133 | } 134 | 135 | Will output "Hello world". The equivalent in Javascript outputs " Helloworld" as the indexes aren't kept in order. 136 | 137 | If you need arrays to stay in order you should use integer keys only. 138 | 139 | 140 | 141 | Unset is fragile 142 | ---------------- 143 | 144 | The `unset` command in PHP works on any variable. PHP-To-Javascript converts it to the Javascript function delete, which only works on objects. This is okay for now as all arrays are currently created as objects, but it is a very fragile way of doing things. I would recommend not using unset, but instead copy out the values you want to keep into a new array. 145 | 146 | 147 | List not supported 148 | ------------------ 149 | 150 | The PHP construct `list` is not supported. 151 | 152 | 153 | Exception model is different 154 | ---------------------------- 155 | 156 | Javascript doesn't have a native way of catching different exception types, and doing different things with them. Although a [different way of implementing this](https://github.com/Danack/PHP-to-Javascript/issues/52) has been suggested, this isn't implemented yet and would require namespaces (which are also not implemented yet) to work. 157 | 158 | 159 | 160 | Pull requests 161 | ============= 162 | 163 | For various reasons pull requests are not accepted on this project. If you find a bug please just open an issue. If there's an enhancement you'd like to see, please open an issue first to discuss it. 164 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base-reality/php-to-javascript", 3 | "description": "A PHP to Javascript converter for automatically converting PHP source code (including Classes) into Javascript.", 4 | "license": "CC-BY-NC-SA-3.0", 5 | "autoload": { 6 | "psr-0": { 7 | "PHPToJavascript": "src/" 8 | } 9 | }, 10 | "require": { 11 | "php": ">=5.4.0" 12 | }, 13 | "authors": [ 14 | { 15 | "name": "Dan Ackroyd", 16 | "email": "Danack@basereality.com", 17 | "homepage": "http://www.basereality.com" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | 2 | The code for PHP-to-Javascript is released under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) license. 3 | 4 | The full details of this license can be read at http://creativecommons.org/licenses/by-nc-sa/3.0/ 5 | 6 | You are free: 7 | to Share — to copy, distribute and transmit the work 8 | to Remix — to adapt the work 9 | 10 | Under the following conditions: 11 | Attribution — You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). 12 | Noncommercial — You may not use this work for commercial purposes. 13 | Share Alike — If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one. 14 | 15 | 16 | Commercial licenses for PHP-to-Javascript will soon be available. 17 | 18 | 19 | To put the above into some real world examples: 20 | 21 | * If you want to use it for your own personal website - no license needed. 22 | 23 | * If you want to use it at work for code you're being paid for - please buy a license. 24 | 25 | * If you want to deploy it on a client's website - please get the client to buy a license. -------------------------------------------------------------------------------- /src/PHPToJavascript/ArrayScope.php: -------------------------------------------------------------------------------- 1 | variableFlag = $variableFlag; 27 | } 28 | 29 | /** 30 | * @param $variableName 31 | * @param $variableFlags 32 | * @return mixed 33 | * 34 | * For a given variable name, try to find the variable in the current scope. 35 | */ 36 | function getScopedVariableForScope($variableName, $variableFlags) { 37 | //Array scopes don't contain variables. 38 | return NULL; 39 | } 40 | 41 | function setVariableName($variableName) { 42 | $this->variableName = $variableName; 43 | } 44 | 45 | 46 | function pushParens(){ 47 | $this->parensCount += 1; 48 | } 49 | 50 | function popParens(){ 51 | $this->parensCount -= 1; 52 | if($this->parensCount <= 0){ 53 | return TRUE; 54 | } 55 | return FALSE; 56 | } 57 | 58 | function getType(){ 59 | return CODE_SCOPE_ARRAY; 60 | } 61 | 62 | function getJS(){ 63 | $js = parent::getJS(); 64 | 65 | $firstOpenParens = strpos($js, "("); 66 | $lastCloseParens = strrpos($js, ")"); 67 | 68 | if($firstOpenParens !== FALSE){ 69 | $js = substr_replace($js, '{', $firstOpenParens, 1); 70 | } 71 | if($lastCloseParens !== FALSE){ 72 | $js = substr_replace($js, '}', $lastCloseParens, 1); 73 | } 74 | 75 | if ($this->parentScope instanceof ClassScope) { 76 | $this->parentScope->setVariableString($this->variableName, $js); 77 | return "/* ".$this->variableName." */"; 78 | } 79 | 80 | return $js; 81 | } 82 | 83 | function fixupArrayIndex(){ 84 | $replace = ''; 85 | 86 | if($this->doubleArrayUsed == false){ 87 | $replace = "".$this->keyCount." : "; 88 | $this->keyCount++; 89 | } 90 | 91 | //Find the array element start position and replace it. 92 | for($x=count($this->jsElements) - 1 ; $x >= 0 ; $x--){ 93 | if($this->jsElements[$x] == ARRAY_ELEMENT_START_MAGIC){ 94 | $this->jsElements[$x] = $replace; 95 | break; 96 | } 97 | } 98 | 99 | $this->doubleArrayUsed = false; 100 | $this->arrayElementStarted = false; 101 | } 102 | 103 | //Contains hacks 104 | function preStateMagic($name, $value){ 105 | parent::preStateMagic($name, $value); 106 | 107 | if($this->arrayElementStarted == false){ 108 | if ($name == 'T_LNUMBER' || 109 | $name == 'T_VARIABLE' || 110 | $name == 'T_CONSTANT_ENCAPSED_STRING' 111 | || $name == 'T_ARRAY' 112 | || $name == '[' 113 | ){ //another embedded array. 114 | $this->addJS(ARRAY_ELEMENT_START_MAGIC); 115 | $this->arrayElementStarted = true; 116 | } 117 | 118 | //TODO - this needs to happen when the double arrow is encountered. 119 | // if ($name == 'T_LNUMBER'){ 120 | // // If someone is mixing automatic with numbered arrays, attempt to support it 121 | // // by continuing the automatic key after their numbered key 122 | // $this->keyCount = intval($value) + 1; 123 | // } 124 | } 125 | 126 | if($name == 'T_DOUBLE_ARROW'){ 127 | $this->doubleArrayUsed = TRUE; 128 | } 129 | 130 | if ($name == ',' || 131 | $name == ')'){ 132 | //Array element has ended. 133 | $this->fixupArrayIndex(); 134 | } 135 | } 136 | 137 | //Contains hacks 138 | function postStateMagic($name, $value){ 139 | parent::postStateMagic($name, $value); 140 | } 141 | 142 | function incrementSquareBracketCount() { 143 | $this->squareBracketCount++; 144 | // if(PHPToJavascript::$TRACE){ 145 | // echo "inc squareBracketCount ".$this->squareBracketCount."\n"; 146 | // } 147 | } 148 | 149 | function decrementSquareBracketCount() { 150 | $this->squareBracketCount--; 151 | // if(PHPToJavascript::$TRACE){ 152 | // echo "dec squareBracketCount ".$this->squareBracketCount."\n"; 153 | // } 154 | } 155 | 156 | function getSquareBracketCount() { 157 | return $this->squareBracketCount; 158 | } 159 | } 160 | 161 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CatchScope.php: -------------------------------------------------------------------------------- 1 | scopedVariables) == TRUE){ 19 | //$variableFlag = $this->scopedVariables[$cVar]; 20 | if($variableFlags & DECLARATION_TYPE_STATIC){ 21 | return $this->name.".".$variableName; 22 | } 23 | else if(($variableFlags & DECLARATION_TYPE_CLASS)){ 24 | if(strpos($variableName, "$") !== FALSE){ 25 | //it's a variable variable like "this->$var"; 26 | return 'this['.$variableName.']'; 27 | } 28 | else{ 29 | return 'this.'.$variableName; 30 | } 31 | } 32 | 33 | return $variableName; 34 | } 35 | 36 | return NULL; 37 | } 38 | 39 | function getType() { 40 | return CODE_SCOPE_CATCH; 41 | } 42 | 43 | function getJS(){ 44 | 45 | $js = ""; 46 | $jsRaw = parent::getJS(); 47 | 48 | $search = array(); 49 | $replace = array(); 50 | 51 | foreach($this->exceptionNames as $exceptionName){ 52 | $search[] = "".$exceptionName.".getMessage()"; 53 | $replace[] = "".$exceptionName.".message"; 54 | } 55 | 56 | $jsRaw = str_replace($search, $replace, $jsRaw); 57 | $js .= $jsRaw; 58 | return $js; 59 | } 60 | 61 | 62 | function addExceptionName($value){ 63 | $this->exceptionNames[] = $value; 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /src/PHPToJavascript/ClassScope.php: -------------------------------------------------------------------------------- 1 | privateVariables)) { 25 | // return $this->privateVariables[$variableName]; 26 | // } 27 | // 28 | // if (array_key_exists($variableName, $this->publicVariables)) { 29 | // return $this->publicVariables[$variableName]; 30 | // } 31 | // } 32 | 33 | 34 | function addParent($value){ 35 | $this->parentClasses[] = $value; 36 | } 37 | 38 | function getType(){ 39 | return CODE_SCOPE_CLASS; 40 | } 41 | 42 | function getEndOfScopeJS(){ 43 | $js = ""; 44 | $js .= "\n"; 45 | $js .= $this->getClassInheritanceJS(); 46 | $js .= "\n"; 47 | $js .= $this->getClassVariableInitJS(); 48 | return $js; 49 | } 50 | 51 | function getClassInheritanceJS(){ 52 | $js = ""; 53 | 54 | if(count($this->parentClasses) > 0){ 55 | $js .= "\n\n"; 56 | 57 | foreach($this->parentClasses as $parentClass){ 58 | $childClass = $this->name; 59 | //Inheritance pattern taken from 60 | //https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript 61 | 62 | $js .= "// inherit $parentClass\n"; 63 | $js .= "$childClass.prototype = new $parentClass();\n"; 64 | 65 | $js .= "// correct the constructor pointer because it points to $parentClass\n"; 66 | $js .= "$childClass.prototype.constructor = $childClass;\n"; 67 | 68 | $js .= "//Need to copy the static functions across and replace the parent class name with the child class name.\n"; 69 | 70 | $js .= "$.extend($childClass, $parentClass);\n"; 71 | } 72 | 73 | $js .= "\n"; 74 | } 75 | 76 | return $js; 77 | } 78 | 79 | 80 | function getClassVariableInitJS(){ 81 | $js = ""; 82 | 83 | // foreach($this->publicVariables as $name => $value){ 84 | // if($value === false){ 85 | // $value = 'null'; 86 | // } 87 | // 88 | // $js .= $this->name.".prototype.".$name." = $value;\n"; 89 | // } 90 | 91 | foreach($this->staticVariables as $name => $value){ 92 | if($value === false){ 93 | $value = 'null'; 94 | } 95 | 96 | $js .= $this->name.".".$name." = $value;\n"; 97 | } 98 | 99 | return $js; 100 | } 101 | 102 | function getJSForClassInPlace(){ 103 | 104 | $jsArray = array(); 105 | 106 | foreach($this->jsElements as $jsElement){ 107 | if($jsElement instanceof CodeScope){ 108 | $jsArray[] = $jsElement->getInPlaceJS(); 109 | } 110 | else if(is_string($jsElement)){ 111 | $jsArray[] = $jsElement; 112 | } 113 | else{ 114 | throw new \Exception("Unknown type in this->jsElements of type [".get_class($jsElement)."]"); 115 | } 116 | } 117 | 118 | $lastElement = array_pop($jsArray); //Always (?) going to be '}' so not sure if worth doing properly 119 | 120 | foreach($this->publicVariables as $name => $value){ 121 | if($value === false){ 122 | $value = 'null'; 123 | } 124 | $jsArray[] = 'this.'.$name." = $value;\n"; 125 | } 126 | 127 | 128 | 129 | $jsArray[] = END_OF_CLASS_POSITION_MARKER; 130 | $jsArray[] = $lastElement; 131 | 132 | return implode($jsArray); 133 | } 134 | 135 | function getJS(){ 136 | 137 | $js = ""; 138 | $js .= $this->getJSForClassInPlace(); 139 | $js .= "\n"; 140 | $js .= $this->getEndOfScopeJS(); 141 | $js .= "\n"; 142 | $js .= $this->getChildDelayedJS(); 143 | 144 | $js = $this->replaceConstructorInJS($js); 145 | 146 | $js = $this->manglePrivateFunctions($js); 147 | 148 | return $js; 149 | } 150 | 151 | function getChildDelayedJS(){ 152 | $js = ""; 153 | 154 | foreach($this->jsElements as $jsElement){ 155 | if($jsElement instanceof CodeScope){ 156 | $js .= $jsElement->getDelayedJS($this->getName()); 157 | $js .= "\n"; 158 | } 159 | } 160 | 161 | return $js; 162 | } 163 | 164 | 165 | /** 166 | * Change function calls to private functions from "this.privateFunction()" to 167 | * "privateFunction()" as that is how they need to be called in Javascript OO layout. 168 | * 169 | * @param $js 170 | * @return mixed 171 | */ 172 | function manglePrivateFunctions($js){ 173 | 174 | $search = array(); 175 | $replace = array(); 176 | 177 | foreach($this->jsElements as $jsElement){ 178 | if($jsElement instanceof FunctionParameterScope){ 179 | 180 | /** @var $functionParameterScope FunctionParameterScope */ 181 | $functionParameterScope = $jsElement; 182 | 183 | if(($functionParameterScope->variableFlag & DECLARATION_TYPE_PRIVATE) != 0){ 184 | //echo "You touched my privates ".$functionParameterScope->getName(); 185 | $search[] = "this.".$functionParameterScope->getName()."("; 186 | $replace[] = "".$functionParameterScope->getName()."("; 187 | } 188 | } 189 | } 190 | 191 | return str_replace($search, $replace, $js); 192 | } 193 | 194 | /** 195 | * The constructor method for a 'class' in Javascript is actually just inlined with the class scope, 196 | * rather than being a function inside the class. This function moves the constructor code from the function 197 | * to the class scope, so that it's in the correct place. 198 | * @param $js 199 | * @return mixed 200 | */ 201 | function replaceConstructorInJS($js){ 202 | $constructor = false; 203 | 204 | foreach($this->jsElements as $jsElement){ 205 | if($jsElement instanceof CodeScope){ 206 | if($jsElement->getName() == '__construct'){ 207 | $constructor = $jsElement->getJS(); 208 | break; 209 | } 210 | } 211 | } 212 | 213 | $parentConstructor = ""; 214 | 215 | foreach($this->parentClasses as $parentClass){ 216 | $parentConstructor .= "".$parentClass.".call(this);\n"; 217 | } 218 | 219 | if($constructor !== false){ 220 | $constructorInfo = trimConstructor($constructor); 221 | $constructorInfo['body'] = $parentConstructor.$constructorInfo['body']; 222 | $js = str_replace(CONSTRUCTOR_PARAMETERS_POSITION, $constructorInfo['parameters'], $js); 223 | $js = str_replace(END_OF_CLASS_POSITION_MARKER, $constructorInfo['body'], $js); 224 | } 225 | else{ 226 | //There is no constructor - just remove the magic strings 227 | $js = str_replace(CONSTRUCTOR_PARAMETERS_POSITION, '', $js); 228 | $js = str_replace(END_OF_CLASS_POSITION_MARKER, $parentConstructor, $js); 229 | } 230 | 231 | return $js; 232 | } 233 | 234 | 235 | /** 236 | * Mark where methods start, so we can put the class constructor here. 237 | */ 238 | function markMethodsStart(){ 239 | if($this->methodsStartIndex === false){ 240 | $this->methodsStartIndex = count($this->jsElements); 241 | //$this->addJS(CONSTRUCTOR_POSITION_MARKER); 242 | } 243 | } 244 | 245 | function getScopedVariableForScope($variableName, $variableFlags){ 246 | $cVar = cvar($variableName); 247 | 248 | if(array_key_exists($cVar, $this->scopedVariables) == true){ 249 | //$variableFlag = $this->scopedVariables[$cVar]; 250 | 251 | if ($variableFlags & DECLARATION_TYPE_CLASS){ 252 | if($variableFlags & DECLARATION_TYPE_PRIVATE){ 253 | return $variableName; 254 | } 255 | if($variableFlags & DECLARATION_TYPE_STATIC){ 256 | return $variableName; 257 | } 258 | if($variableFlags & DECLARATION_TYPE_PUBLIC){ 259 | //TODO should the 'this' be here, or in the T_OBJECTOPERATOR 260 | return 'this.'.$variableName; 261 | } 262 | } 263 | } 264 | 265 | if ($variableFlags & DECLARATION_TYPE_CLASS) { 266 | if ($variableFlags & DECLARATION_TYPE_STATIC) { 267 | return $variableName; 268 | } 269 | else { 270 | //Either a function or property set below where it is defined. 271 | // OR it could be a variable that is defined in the parent class' scope. 272 | return 'this.'.$variableName; 273 | } 274 | } 275 | 276 | return null; 277 | } 278 | 279 | function addStaticVariable($variableName){ 280 | $this->staticVariables[$variableName] = false; 281 | $this->currentVariableForConcattingValue = &$this->staticVariables[$variableName]; 282 | 283 | $this->currentVariableName = $variableName; 284 | } 285 | 286 | function addPublicVariable($variableName){ 287 | $this->publicVariables[$variableName] = false; 288 | $this->currentVariableForConcattingValue = &$this->publicVariables[$variableName]; 289 | $this->currentVariableName = $variableName; 290 | } 291 | 292 | function addPrivateVariable($variableName) { 293 | $this->privateVariables[$variableName] = false; 294 | $this->currentVariableForConcattingValue = &$this->privateVariables[$variableName]; 295 | $this->currentVariableName = $variableName; 296 | } 297 | 298 | function setVariableString($variableName, $string) { 299 | if (array_key_exists($variableName, $this->staticVariables) == true) { 300 | $this->staticVariables[$variableName] = $string; 301 | return; 302 | } 303 | 304 | if (array_key_exists($variableName, $this->publicVariables) == true) { 305 | $this->publicVariables[$variableName] = $string; 306 | return; 307 | } 308 | 309 | throw new \Exception("Variable [$variableName] not known - cannot set it's string value."); 310 | } 311 | 312 | 313 | 314 | /** 315 | * For class variables that are added to the class scope, but are delayed to be declared outside 316 | * the function (to be public or static) we need to grab the default values to be able to set 317 | * the variables to them. Incidentally grabs any comments. 318 | * 319 | * @param $value 320 | * @throws \Exception 321 | */ 322 | function addToVariableValue($value){ 323 | if($this->currentVariableForConcattingValue === null){ 324 | throw new \Exception("Trying to concat [$value] to the current variable - but it's not set. "); 325 | } 326 | 327 | if($this->currentVariableForConcattingValue === false){ 328 | $this->currentVariableForConcattingValue = ''; 329 | } 330 | 331 | $this->currentVariableForConcattingValue .= $value; 332 | } 333 | 334 | function getDelayedJS($parentScopeName){ 335 | $output = ""; 336 | 337 | // foreach($this->publicVariables as $name => $value){ 338 | // if($value === false){ 339 | // $value = 'null'; 340 | // } 341 | // $output .= $this->name.".prototype.".$name." = $value;\n"; 342 | // } 343 | 344 | foreach($this->staticVariables as $name => $value){ 345 | if($value === false){ 346 | $value = 'null'; 347 | } 348 | $output .= $this->name.".".$name." = $value;\n"; 349 | } 350 | 351 | return $output; 352 | } 353 | } 354 | 355 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState.php: -------------------------------------------------------------------------------- 1 | stateMachine = $stateMachine; 15 | } 16 | 17 | function changeToState($newState, $extraParams = array()){ 18 | $this->stateMachine->changeToState($newState, $extraParams); 19 | } 20 | 21 | public function enterState($extraParams = array()){ 22 | } 23 | 24 | /** 25 | * @param $name 26 | * @param $value 27 | * @return bool Whether the token should be reprocessed by the new state 28 | */ 29 | abstract function processToken($name, $value, $parsedToken); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/ARRAY.php: -------------------------------------------------------------------------------- 1 | stateMachine->currentScope instanceof FunctionParameterScope){ 11 | if($this->stateMachine->currentScope->beforeVariable == true){ 12 | $this->stateMachine->addJS("/*".$value."*/"); 13 | $this->changeToState(CONVERTER_STATE_DEFAULT); 14 | return; 15 | } 16 | } 17 | 18 | $classScope = false; 19 | 20 | $this->stateMachine->startArrayScope($value, false); 21 | 22 | //$this->changeToState(CONVERTER_STATE_DEFAULT); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/CLASS.php: -------------------------------------------------------------------------------- 1 | stateMachine->pushScope(CODE_SCOPE_CLASS, $value); 10 | $this->stateMachine->addJS("function $value(".CONSTRUCTOR_PARAMETERS_POSITION.")"); 11 | $this->changeToState(CONVERTER_STATE_DEFAULT); 12 | } 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/CLOSEPARENS.php: -------------------------------------------------------------------------------- 1 | stateMachine->currentScope instanceof FunctionParameterScope || 9 | $this->stateMachine->currentScope instanceof CatchScope){ 10 | $this->stateMachine->pushScope( 11 | CODE_SCOPE_FUNCTION, 12 | $this->stateMachine->currentScope->getName() 13 | ); 14 | } 15 | 16 | $this->stateMachine->addJS(')'); 17 | 18 | $this->changeToState(CONVERTER_STATE_DEFAULT); 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/CapturingDefaultValue.php: -------------------------------------------------------------------------------- 1 | stateMachine->changeToState(CONVERTER_STATE_DEFAULT); 14 | return true; 15 | } 16 | else{ 17 | 18 | if ($name == 'T_STRING' || 19 | $name == 'T_CONSTANT_ENCAPSED_STRING') { 20 | $this->stateMachine->currentScope->addToJsForPreviousVariable($value); 21 | } 22 | else { 23 | $this->stateMachine->currentScope->addToJsForPreviousVariable($parsedToken); 24 | } 25 | } 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/Comma.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS(','); 10 | 11 | if($this->stateMachine->currentScope instanceof FunctionParameterScope){ 12 | $this->stateMachine->currentScope->setBeforeVariable(true); 13 | } 14 | 15 | $this->changeToState(CONVERTER_STATE_DEFAULT); 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/Default.php: -------------------------------------------------------------------------------- 1 | CONVERTER_STATE_ECHO, 12 | 'T_ARRAY' => CONVERTER_STATE_ARRAY, 13 | 'T_CLASS' => CONVERTER_STATE_CLASS, 14 | 15 | 'T_TRAIT' => CONVERTER_STATE_CLASS, //traits are effectively the same as classes 16 | 'T_FUNCTION' => CONVERTER_STATE_FUNCTION, 17 | 'T_FOREACH' => CONVERTER_STATE_FOREACH, 18 | 'T_PUBLIC' => CONVERTER_STATE_PUBLIC, 19 | 'T_VARIABLE' => CONVERTER_STATE_VARIABLE, 20 | 'T_STATIC' => CONVERTER_STATE_STATIC, 21 | 'T_STRING' => CONVERTER_STATE_STRING, 22 | 'T_VAR' => CONVERTER_STATE_T_PUBLIC, 23 | 'T_PRIVATE' => CONVERTER_STATE_T_PRIVATE, 24 | 25 | 'T_EXTENDS' => CONVERTER_STATE_T_EXTENDS, 26 | 'T_USE' => CONVERTER_STATE_T_USE, 27 | 28 | 'T_NEW' => CONVERTER_STATE_T_NEW, 29 | 'T_CONSTANT_ENCAPSED_STRING' => CONVERTER_STATE_VARIABLE_DEFAULT, 30 | '=' => CONVERTER_STATE_EQUALS, 31 | ')' => CONVERTER_STATE_CLOSE_PARENS, 32 | 'T_REQUIRE_ONCE' => CONVERTER_STATE_REQUIRE, 33 | 'T_IMPLEMENTS' => CONVERTER_STATE_IMPLEMENTS_INTERFACE, 34 | 35 | 'T_ABSTRACT' => CONVERTER_STATE_ABSTRACT, 36 | 37 | 'T_INTERFACE' => CONVERTER_STATE_INTERFACE, 38 | 'T_OBJECT_OPERATOR' => CONVERTER_STATE_OBJECT_OPERATOR, 39 | ',' => CONVERTER_STATE_COMMA, 40 | 'T_DOUBLE_ARROW' => CONVERTER_STATE_DOUBLE_ARROW, 41 | 'T_DOUBLE_COLON' => CONVERTER_STATE_DOUBLE_COLON, 42 | 'T_NAMESPACE' => CONVERTER_STATE_NAME_SPACE, 43 | 'T_UNSET' => CONVERTER_STATE_T_UNSET, 44 | 'T_TRY' => CONVERTER_STATE_T_TRY, 45 | 'T_CATCH' => CONVERTER_STATE_T_CATCH, 46 | 'T_GLOBAL' => CONVERTER_STATE_GLOBAL, 47 | 'T_DOLLAR_OPEN_CURLY_BRACES' => CONVERTER_STATE_EMBEDDED_VARIABLE, 48 | ); 49 | 50 | function processToken($name, $value, $parsedToken){ 51 | if($name == 'T_STRING'){ 52 | if($value == 'define'){ 53 | $this->changeToState(CONVERTER_STATE_DEFINE); 54 | return TRUE; 55 | } 56 | } 57 | 58 | if ($name == '[') { 59 | //echo "We were in default state - but encountered a ["; 60 | 61 | $previousName = false; 62 | $previousValue = false; 63 | $this->stateMachine->getPreviousNonWhitespaceToken($previousName, $previousValue); 64 | 65 | if ($previousName == '=' || 66 | $previousName == 'T_DOUBLE_ARROW' || 67 | $previousName == ',' || 68 | $previousName == '(' || 69 | $previousName == '[' ) { //Yep, this is the start of an array declaration. 70 | $this->stateMachine->startArrayScope("", true); 71 | 72 | //$this->stateMachine->currentScope->incrementSquareBracketCount(); 73 | 74 | $this->stateMachine->currentTokenStream->insertToken('('); 75 | return false; 76 | } 77 | else if ($this->stateMachine->currentScope instanceof ArrayScope) { 78 | $this->stateMachine->currentScope->incrementSquareBracketCount(); 79 | } 80 | } 81 | 82 | if ($name == ']') { 83 | if ($this->stateMachine->currentScope instanceof ArrayScope) { 84 | $this->stateMachine->currentScope->decrementSquareBracketCount(); 85 | 86 | if ($this->stateMachine->currentScope->getSquareBracketCount() <= 0) { 87 | if ($this->stateMachine->currentScope->startedBySquareBracket) { 88 | $this->stateMachine->currentTokenStream->insertToken(')'); 89 | return false; 90 | } 91 | } 92 | $this->stateMachine->currentScope->addJS(']'); 93 | return false; 94 | } 95 | } 96 | 97 | 98 | 99 | if(array_key_exists($name, $this->tokenStateChangeList) == TRUE){ 100 | $this->changeToState($this->tokenStateChangeList[$name]); 101 | return TRUE; 102 | } 103 | 104 | if($name == 'T_LNUMBER'){ 105 | if($this->stateMachine->currentScope instanceof FunctionParameterScope){ 106 | //It's a number as a placeholder for a param e.g. 107 | //function someFunction($foo = 5){...} 108 | $this->changeToState(CONVERTER_STATE_VARIABLE_DEFAULT); 109 | return TRUE; 110 | } 111 | } 112 | 113 | $js = $parsedToken; 114 | $this->stateMachine->addJS($js); 115 | 116 | if($name == '{'){ 117 | if($this->stateMachine->currentScope->startOfFunction() == TRUE){ 118 | $this->stateMachine->addDefaultsForVariables(); 119 | } 120 | } 121 | 122 | return FALSE; 123 | } 124 | } 125 | 126 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/DoubleArrow.php: -------------------------------------------------------------------------------- 1 | stateMachine->currentScope instanceof ArrayScope){ 10 | // $this->stateMachine->currentScope->markKeyValueSeparator(); 11 | // } 12 | // else{ 13 | // //Is a doubleArrow possible anywhere else in PHP code? 14 | // } 15 | 16 | $this->stateMachine->addJS(':'); 17 | 18 | $this->changeToState(CONVERTER_STATE_DEFAULT); 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/Echo.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS(self::$echoConversionFunction); 32 | $this->stateMachine->addJS($parsedToken); 33 | 34 | $this->stateMachine->setPendingSymbol(';', ")"); 35 | $this->changeToState(CONVERTER_STATE_DEFAULT); 36 | return FALSE; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/EmbeddedVariable.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS('" + '); 12 | } 13 | 14 | function processToken($name, $value, $parsedToken){ 15 | 16 | if ($name == 'T_STRING_VARNAME'){ 17 | $scopedVariableName = $this->stateMachine->getVariableNameForScope($value, 0); 18 | $this->stateMachine->addJS($scopedVariableName); 19 | } 20 | 21 | if ($name == '}') { 22 | 23 | $this->stateMachine->addJS(' + "'); 24 | $this->stateMachine->changeToState(CONVERTER_STATE_DEFAULT); 25 | } 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/EndOfClass.php: -------------------------------------------------------------------------------- 1 | previousScope = $extraParams['previousScope']; 12 | } 13 | 14 | function processToken($name, $value, $parsedToken){ 15 | if($name == '}'){ 16 | $this->stateMachine->addJS('}'."\n\n"); 17 | $className = $this->previousScope->name; 18 | // $this->stateMachine->addJS("$className = new $className(/*Constuctor for static methods+vars*/);"."\n\n"); 19 | } 20 | // else{ 21 | // throw new Exception( "Only token } should be getting here."); 22 | // } 23 | 24 | $this->changeToState(CONVERTER_STATE_DEFAULT); 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/Equals.php: -------------------------------------------------------------------------------- 1 | stateMachine->currentScope instanceof FunctionParameterScope){ 9 | // //Don't add an equals - default parameters are set inside the JS function 10 | // 11 | // $this->changeToState(CONVERTER_STATE_CAPTURING_DEFAULT_VALUE); 12 | // 13 | // } 14 | // else{ 15 | $this->stateMachine->addJS($name); 16 | // } 17 | 18 | $this->changeToState(CONVERTER_STATE_DEFAULT); 19 | } 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/FUNCTION.php: -------------------------------------------------------------------------------- 1 | stateMachine->pushScope(CODE_SCOPE_FUNCTION_PARAMETERS, '', $this->stateMachine->variableFlags); 14 | $this->stateMachine->addJS("function "); 15 | $this->stateMachine->clearVariableFlags(); 16 | $this->changeToState(CONVERTER_STATE_DEFAULT); 17 | return true; 18 | } 19 | 20 | 21 | if($name == "T_STRING"){ 22 | 23 | $previousScope = $this->stateMachine->currentScope; 24 | 25 | $this->stateMachine->pushScope(CODE_SCOPE_FUNCTION_PARAMETERS, $value, $this->stateMachine->variableFlags); 26 | 27 | if($previousScope instanceof ClassScope){ 28 | $previousScope->markMethodsStart(); 29 | 30 | if($this->stateMachine->variableFlags & DECLARATION_TYPE_PRIVATE){ 31 | $this->stateMachine->addJS("function $value "); 32 | } 33 | else{ 34 | $this->stateMachine->addJS(PUBLIC_FUNCTION_MARKER_MAGIC_STRING."$value = function "); 35 | } 36 | } 37 | else{ 38 | $this->stateMachine->addJS("function $value "); 39 | } 40 | 41 | $this->stateMachine->clearVariableFlags(); 42 | $this->changeToState(CONVERTER_STATE_DEFAULT); 43 | } 44 | } 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/ImportNamespace.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS('/*'); 9 | } 10 | 11 | function processToken($name, $value, $parsedToken){ 12 | if($name == ';'){ 13 | $this->stateMachine->addJS('*/'); 14 | $this->changeToState(CONVERTER_STATE_DEFAULT); 15 | return; 16 | } 17 | 18 | $this->stateMachine->addJS($value); 19 | //parse the namespace 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/REQUIRE.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS('// Opening the require'.$value); 11 | 12 | $this->changeToState(CONVERTER_STATE_DEFAULT); 13 | $this->stateMachine->requireFile($value); 14 | } 15 | 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/SkipToSemiColon.php: -------------------------------------------------------------------------------- 1 | changeToState(CONVERTER_STATE_DEFAULT); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TABSTRACT.php: -------------------------------------------------------------------------------- 1 | stateMachine->currentScope instanceof GlobalScope){ 11 | //Do nothing - abstract classes don't affect JS code generation. 12 | $this->changeToState(CONVERTER_STATE_DEFAULT); 13 | } 14 | 15 | if($this->stateMachine->currentScope instanceof ClassScope){ 16 | //Abstract functions inside a class are commented out 17 | $this->changeToState(CONVERTER_STATE_ABSTRACT_FUNCTION); 18 | return TRUE; 19 | } 20 | 21 | $this->changeToState(CONVERTER_STATE_DEFAULT); 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TABSTRACTREMOVE.php: -------------------------------------------------------------------------------- 1 | first = TRUE; 10 | 11 | $this->stateMachine->addJS("//"); 12 | } 13 | 14 | function processToken($name, $value, $parsedToken){ 15 | $this->stateMachine->addJS('//'.$value); 16 | 17 | if($name == ';'){ 18 | $this->changeToState(CONVERTER_STATE_DEFAULT); 19 | } 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TCATCH.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS('catch'); 19 | $this->stateMachine->pushScope( 20 | CODE_SCOPE_CATCH, 21 | 'catch' 22 | ); 23 | $this->changeToState(CONVERTER_STATE_DEFAULT); 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TDOUBLECOLON.php: -------------------------------------------------------------------------------- 1 | stateMachine->addVariableFlags(DECLARATION_TYPE_CLASS); 10 | $this->stateMachine->addVariableFlags(DECLARATION_TYPE_STATIC); 11 | 12 | $this->stateMachine->addJS('.'); 13 | $this->changeToState(CONVERTER_STATE_DEFAULT); 14 | } 15 | } 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TEXTENDS.php: -------------------------------------------------------------------------------- 1 | stateMachine->currentScope->addParent($value); 13 | } 14 | 15 | if($name == '{'){ 16 | $this->changeToState(CONVERTER_STATE_DEFAULT); 17 | return TRUE; 18 | } 19 | 20 | // This would support use 21 | /*if($name == ';'){ 22 | if($this->extendsName == null){ 23 | throw new \Exception("Didn't find class name to USE."); 24 | } 25 | 26 | $this->stateMachine->currentScope->addParent($this->extendsName); 27 | //$this->extendsName = $value; 28 | $this->changeToState(CONVERTER_STATE_DEFAULT); 29 | return;//don't need to include the ';' 30 | }*/ 31 | } 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TFOREACH.php: -------------------------------------------------------------------------------- 1 | arrayElements = array(); 23 | $this->keyOrValueElements = array(); 24 | $this->valueElements = array(); 25 | 26 | $this->subState = CodeConverterState_TFOREACH::$SUBSTATE_OBJECT; 27 | } 28 | 29 | function processToken($name, $value, $parsedToken){ 30 | 31 | $jsToAdd = FALSE; 32 | 33 | //TODO - this is repeating code elsewhere. 34 | if ($name == 'T_VARIABLE'){ 35 | $jsToAdd = cVar($value); 36 | } 37 | else if ($name == 'T_OBJECT_OPERATOR' || 38 | $name == 'T_DOUBLE_COLON'){ 39 | $jsToAdd = '.'; 40 | } 41 | else if ($name == 'T_STRING'){ 42 | if (strtolower($value) == 'self') { 43 | $jsToAdd = $this->stateMachine->getClassName(); 44 | } 45 | else{ 46 | $jsToAdd = $value; 47 | } 48 | } 49 | else if ($name == 'T_WHITESPACE'){ 50 | $jsToAdd = $value; 51 | } 52 | else if($name == ')'){ 53 | //Bracket is added magically, when we write the foreach from the extracted names. 54 | //$jsToAdd = $name; 55 | } 56 | 57 | if($name == 'T_AS'){ 58 | $this->subState = CodeConverterState_TFOREACH::$SUBSTATE_KEY_OR_VALUE; 59 | } 60 | 61 | if($name == 'T_DOUBLE_ARROW'){ 62 | $this->subState = CodeConverterState_TFOREACH::$SUBSTATE_VALUE; 63 | } 64 | 65 | if($jsToAdd != FALSE){ 66 | switch($this->subState){ 67 | 68 | case(CodeConverterState_TFOREACH::$SUBSTATE_OBJECT):{ 69 | $this->arrayElements[] = $jsToAdd; 70 | break; 71 | } 72 | 73 | case(CodeConverterState_TFOREACH::$SUBSTATE_KEY_OR_VALUE):{ 74 | $this->keyOrValueElements[] = $jsToAdd; 75 | break; 76 | } 77 | 78 | case(CodeConverterState_TFOREACH::$SUBSTATE_VALUE):{ 79 | $this->valueElements[] = $jsToAdd; 80 | break; 81 | } 82 | } 83 | } 84 | 85 | if ($name == '{') { 86 | $this->finaliseString(); 87 | $this->changeToState(CONVERTER_STATE_DEFAULT); 88 | } 89 | } 90 | 91 | 92 | function finaliseString(){ 93 | 94 | if(count($this->valueElements) == 0){ 95 | $this->finaliseWithoutKey(); 96 | } 97 | else{ 98 | $this->finaliseWithKey(); 99 | } 100 | } 101 | 102 | function finaliseWithoutKey(){ 103 | $array = trim(implode('', $this->arrayElements)); 104 | $value = trim(implode('', $this->keyOrValueElements)); 105 | 106 | $this->stateMachine->addJS( "for (var {$value}Key in $array) {". 107 | " \n var $value = $array"."[{$value}Key];"); 108 | 109 | $this->stateMachine->currentScope->addScopedVariable($value, 0); 110 | } 111 | 112 | function finaliseWithKey(){ 113 | $array = trim(implode('', $this->arrayElements)); 114 | $key = trim(implode('', $this->keyOrValueElements)); 115 | $value = trim(implode('', $this->valueElements)); 116 | 117 | $this->stateMachine->addJS("for (var $key in $array) {". 118 | "\n var $value = $array"."[$key];"); 119 | 120 | $this->stateMachine->currentScope->addScopedVariable($key, 0); 121 | $this->stateMachine->currentScope->addScopedVariable($value, 0); 122 | } 123 | } 124 | 125 | 126 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TGLOBAL.php: -------------------------------------------------------------------------------- 1 | changeToState(CONVERTER_STATE_SKIP_TO_SEMICOLON); 9 | } 10 | } -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TIMPLEMENTSINTERFACE.php: -------------------------------------------------------------------------------- 1 | first = TRUE; 12 | } 13 | 14 | function processToken($name, $value, $parsedToken){ 15 | if($this->first == TRUE){ 16 | $this->first = FALSE; 17 | $this->stateMachine->addJS("/*"); 18 | } 19 | 20 | if($name == 'T_STRING' || $name == 'T_WHITESPACE'){ 21 | $this->stateMachine->addJS($value); 22 | } 23 | 24 | if($name == '{'){ 25 | $this->stateMachine->addJS("*/"); 26 | $this->changeToState(CONVERTER_STATE_DEFAULT); 27 | return TRUE; 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TINTERFACE.php: -------------------------------------------------------------------------------- 1 | first = TRUE; 12 | } 13 | 14 | function processToken($name, $value, $parsedToken){ 15 | if($this->first == TRUE){ 16 | $this->first = FALSE; 17 | $this->stateMachine->addJS("/*"); 18 | } 19 | 20 | if($name == 'T_STRING' || $name == 'T_WHITESPACE'){ 21 | $this->stateMachine->addJS($value); 22 | } 23 | else{ 24 | $this->stateMachine->addJS($name); 25 | } 26 | 27 | if($name == '}'){ 28 | $this->stateMachine->addJS("}*/"); 29 | $this->changeToState(CONVERTER_STATE_DEFAULT); 30 | } 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TNAMESPACE.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS('/*'); 11 | } 12 | 13 | function processToken($name, $value, $parsedToken){ 14 | //$this->stateMachine->addJS('.'); 15 | //$this->stateMachine->addVariableFlags(DECLARATION_TYPE_CLASS); 16 | 17 | if($name == ';'){ 18 | $this->stateMachine->addJS('*/'); 19 | $this->changeToState(CONVERTER_STATE_DEFAULT); 20 | return; 21 | } 22 | 23 | $this->stateMachine->addJS($value); 24 | //parse the namespace 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TNEW.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS('new'); 9 | $this->stateMachine->addVariableFlags(DECLARATION_TYPE_NEW); 10 | $this->changeToState(CONVERTER_STATE_DEFAULT); 11 | } 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TOBJECTOPERATOR.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS("["); 13 | $this->stateMachine->addSymbolAfterNextToken(']'); 14 | } 15 | else{ 16 | $this->stateMachine->addJS("."); 17 | } 18 | 19 | $this->changeToState(CONVERTER_STATE_VARIABLE); 20 | return TRUE; 21 | } 22 | 23 | 24 | if($name == "T_STRING"){ 25 | 26 | $this->stateMachine->addJS("."); 27 | 28 | $this->changeToState(CONVERTER_STATE_DEFAULT); 29 | return TRUE; 30 | } 31 | 32 | //echo "Interesting - name $name value = $value\n"; 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TPRIVATE.php: -------------------------------------------------------------------------------- 1 | stateMachine->variableFlags |= DECLARATION_TYPE_PRIVATE; 10 | 11 | //TODO - should this be if currentScope instanceof? 12 | $this->stateMachine->variableFlags |= DECLARATION_TYPE_CLASS; 13 | 14 | $this->changeToState(CONVERTER_STATE_DEFAULT); 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TPUBLIC.php: -------------------------------------------------------------------------------- 1 | stateMachine->variableFlags |= DECLARATION_TYPE_PUBLIC; 11 | $this->changeToState(CONVERTER_STATE_DEFAULT); 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TSTATIC.php: -------------------------------------------------------------------------------- 1 | stateMachine->variableFlags & DECLARATION_TYPE_NEW){ 11 | $this->stateMachine->addJS("this.prototype.constructor"); 12 | } 13 | else{ 14 | $this->stateMachine->variableFlags |= DECLARATION_TYPE_STATIC; 15 | } 16 | $this->changeToState(CONVERTER_STATE_DEFAULT); 17 | } 18 | } 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TSTRING.php: -------------------------------------------------------------------------------- 1 | stateMachine->isDefined($value)) { 12 | $defineValue = $this->stateMachine->getDefine($value); 13 | $this->stateMachine->addJS($defineValue); 14 | } 15 | //TODO add isClass($value) 16 | else if(strcmp('static', $value) == 0 || 17 | strcmp('self', $value) == 0){ 18 | $this->stateMachine->addJS($this->stateMachine->getClassName()); 19 | } 20 | else if($this->stateMachine->currentScope instanceof FunctionParameterScope){ 21 | //Probably a typehint. 22 | $this->stateMachine->addJS( "/*". $value ."*/"); 23 | } 24 | else if($this->stateMachine->currentScope instanceof CatchScope){ 25 | //$this->stateMachine->currentScope->addExceptionName($value); 26 | $this->stateMachine->addJS( "/*". $value ."*/"); 27 | } 28 | else{ 29 | $variable = $this->stateMachine->getVariableFromScope($value, CODE_SCOPE_CLASS); 30 | 31 | if ($variable) { 32 | if ($variable->flags & DECLARATION_TYPE_PRIVATE) { 33 | if ($this->stateMachine->previousTokensMatch(['this', '.']) == true) { 34 | //For the record, this is the hackiest bit of code, so far. 35 | $this->stateMachine->deleteTokens(2); 36 | } 37 | } 38 | } 39 | 40 | $this->stateMachine->addJS($value); 41 | } 42 | 43 | //TODO - added this to fix "SomeClass::someFunc()" leaving variableFlags in non zero state 44 | //But not sure if this is safe. 45 | $this->stateMachine->variableFlags = 0; 46 | 47 | $this->changeToState(CONVERTER_STATE_DEFAULT); 48 | } 49 | } 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TTRY.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS('try'); 12 | $this->changeToState(CONVERTER_STATE_DEFAULT); 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TUNSET.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS('delete'); 15 | $this->changeToState(CONVERTER_STATE_DEFAULT); 16 | } 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TUSE.php: -------------------------------------------------------------------------------- 1 | extendsName = null; 11 | } 12 | 13 | function processToken($name, $value, $parsedToken){ 14 | 15 | if($this->stateMachine->currentScope instanceof GlobalScope){ 16 | $this->changeToState(CONVERTER_STATE_IMPORT_NAMESPACE); 17 | return true; 18 | } 19 | else if($this->stateMachine->currentScope instanceof ClassScope){ 20 | //$this->changeToState(CONVERTER_STATE_T_EXTENDS); 21 | 22 | if($name == 'T_STRING'){ 23 | //echo "Need to grab variables/functions from [$value]"; 24 | $this->extendsName = $value; 25 | } 26 | if($name == ';'){ 27 | if($this->extendsName == null){ 28 | throw new \Exception("Didn't find class name to USE."); 29 | } 30 | 31 | $this->stateMachine->currentScope->addParent($this->extendsName); 32 | $this->changeToState(CONVERTER_STATE_DEFAULT); 33 | return;//don't need to include the ';' 34 | } 35 | } 36 | else{ 37 | throw new \Exception("use is only expected in Global and class scopes. Don't know what to do with it here."); 38 | } 39 | 40 | 41 | } 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TVARIABLE.php: -------------------------------------------------------------------------------- 1 | stateMachine->currentScope instanceof GlobalScope){ 10 | $this->changeToState(CONVERTER_STATE_VARIABLE_GLOBAL); 11 | return TRUE; 12 | } 13 | 14 | if($this->stateMachine->currentScope instanceof FunctionScope){ 15 | //TODO - Double-check FunctionParameterScope is meant to be the same as FunctionScope 16 | $this->changeToState(CONVERTER_STATE_VARIABLE_FUNCTION); 17 | return TRUE; 18 | } 19 | 20 | if($this->stateMachine->currentScope instanceof FunctionParameterScope){ 21 | $this->changeToState(CONVERTER_STATE_VARIABLE_FUNCTION_PARAMETER); 22 | return TRUE; 23 | } 24 | 25 | if($this->stateMachine->currentScope instanceof ClassScope){ 26 | $this->changeToState(CONVERTER_STATE_VARIABLE_CLASS); 27 | return TRUE; 28 | } 29 | if($this->stateMachine->currentScope instanceof ArrayScope){ 30 | $this->changeToState(CONVERTER_STATE_VARIABLE_ARRAY); 31 | return TRUE; 32 | } 33 | 34 | if($this->stateMachine->currentScope instanceof CatchScope){ 35 | $this->changeToState(CONVERTER_STATE_VARIABLE_CATCH); 36 | return TRUE; 37 | } 38 | 39 | throw new \Exception("Unknown scope [".get_class($this->stateMachine->currentScope)."] - don't know how to process variables."); 40 | } 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TVARIABLEARRAY.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS($variableName); 11 | $this->stateMachine->clearVariableFlags(); 12 | $this->changeToState(CONVERTER_STATE_DEFAULT); 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TVARIABLECATCH.php: -------------------------------------------------------------------------------- 1 | stateMachine->addScopedVariable($variableName, $this->stateMachine->variableFlags); 11 | $this->stateMachine->currentScope->addExceptionName($variableName); 12 | $this->stateMachine->addJS($variableName); 13 | $this->stateMachine->clearVariableFlags(); 14 | $this->changeToState(CONVERTER_STATE_DEFAULT); 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TVARIABLECLASS.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS("this"); 15 | } 16 | 17 | $this->stateMachine->addScopedVariable($variableName, $this->stateMachine->variableFlags); 18 | 19 | if($this->stateMachine->variableFlags & DECLARATION_TYPE_STATIC){ 20 | $this->stateMachine->currentScope->addStaticVariable($variableName); 21 | $this->changeToState(CONVERTER_STATE_VARIABLE_VALUE); 22 | } 23 | else if($this->stateMachine->variableFlags & DECLARATION_TYPE_PUBLIC){ 24 | $this->stateMachine->currentScope->addPublicVariable($variableName); 25 | $this->changeToState(CONVERTER_STATE_VARIABLE_VALUE); 26 | } 27 | else{ 28 | 29 | // if($this->stateMachine->variableFlags & DECLARATION_TYPE_CLASS){ 30 | // //All other variables are treated as private 31 | // $this->stateMachine->currentScope->addScopedVariable($variableName, $this->stateMachine->variableFlags); 32 | // } 33 | 34 | $this->stateMachine->addJS("var "); 35 | $this->stateMachine->addJS($variableName); 36 | $this->stateMachine->clearVariableFlags(); 37 | $this->changeToState(CONVERTER_STATE_DEFAULT); 38 | } 39 | } 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TVARIABLEFUNCTION.php: -------------------------------------------------------------------------------- 1 | isClassVariable = FALSE; 13 | } 14 | 15 | function processToken($name, $value, $parsedToken) { 16 | 17 | if($value == '$this'){ 18 | $this->stateMachine->addJS("this"); 19 | $this->stateMachine->addVariableFlags(DECLARATION_TYPE_CLASS); 20 | return; 21 | } 22 | 23 | $variableName = cVar($value); 24 | $isClassVariable = ($this->stateMachine->variableFlags & DECLARATION_TYPE_CLASS); 25 | 26 | if(($this->stateMachine->variableFlags & DECLARATION_TYPE_STATIC) && 27 | (!($this->stateMachine->variableFlags & DECLARATION_TYPE_CLASS))) { 28 | $this->stateMachine->addScopedVariable($variableName, $this->stateMachine->variableFlags); 29 | 30 | $scopedName = $this->stateMachine->getVariableNameForScope($variableName, $this->stateMachine->variableFlags); 31 | $this->stateMachine->addJS("if (typeof ".$scopedName." == 'undefined')\n "); 32 | } 33 | 34 | 35 | if ($isClassVariable == TRUE && ( 36 | $name == ")" || $name == ',' || $name == ';' 37 | ) 38 | ) { 39 | //keyword 'this' has been passed as a variable e.g. 40 | //json_encode_object(this) 41 | //$this->stateMachine->addJS("this)"); 42 | $this->stateMachine->addJS($name); 43 | } 44 | else if($name == "T_OBJECT_OPERATOR"){ 45 | $this->stateMachine->addJS("."); 46 | } 47 | else if($name == "T_STRING" || 48 | $name == "T_VARIABLE") { 49 | 50 | $scopedVariableName = $this->stateMachine->getVariableNameForScope($variableName, $this->stateMachine->variableFlags); 51 | $enclosedVariableName = $this->stateMachine->encloseVariable($scopedVariableName); 52 | $this->stateMachine->addJS($enclosedVariableName); 53 | $this->stateMachine->variableFlags = 0; 54 | } 55 | else{ 56 | throw new \Exception("Unexpected token in state CodeConverterState_TVARIABLEFUNCTION, wasn't expected an [".$name."]"); 57 | } 58 | 59 | $this->isClassVariable = FALSE; 60 | $this->stateMachine->clearVariableFlags(); 61 | $this->changeToState(CONVERTER_STATE_DEFAULT); 62 | } 63 | } 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TVARIABLEGLOBAL.php: -------------------------------------------------------------------------------- 1 | stateMachine->addJS("this"); 13 | } 14 | 15 | $variableName = cVar($value); 16 | 17 | $wasAdded = $this->stateMachine->addScopedVariable($variableName, $this->stateMachine->variableFlags); 18 | 19 | if($wasAdded == true){ 20 | $this->stateMachine->addJS("var "); 21 | } 22 | 23 | $enclosedVariableName = $this->stateMachine->encloseVariable($variableName); 24 | 25 | $this->stateMachine->addJS($enclosedVariableName); 26 | 27 | $this->stateMachine->clearVariableFlags(); 28 | 29 | $this->changeToState(CONVERTER_STATE_DEFAULT); 30 | } 31 | } 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/TVARIABLEPARAMETER.php: -------------------------------------------------------------------------------- 1 | stateMachine->addScopedVariable($variableName, $this->stateMachine->variableFlags); 12 | $this->stateMachine->addJS($variableName); 13 | $this->changeToState(CONVERTER_STATE_CAPTURING_DEFAULT_VALUE); 14 | } 15 | } 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/VariableDefault.php: -------------------------------------------------------------------------------- 1 | stateMachine->currentScope instanceof FunctionParameterScope){ 11 | //$this->stateMachine->addJS( "/*". $value ."*/"); 12 | $this->stateMachine->currentScope->addToJsForPreviousVariable($value); 13 | } 14 | else{ 15 | $this->stateMachine->addJS($value); 16 | } 17 | 18 | $this->changeToState(CONVERTER_STATE_DEFAULT); 19 | } 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/VariableValue.php: -------------------------------------------------------------------------------- 1 | stateMachine->currentScope->addToVariableValue($value); 17 | return; 18 | } 19 | 20 | if($name == ';'){ 21 | $this->stateMachine->clearVariableFlags(); 22 | $this->changeToState(CONVERTER_STATE_DEFAULT); 23 | return; 24 | } 25 | 26 | if ($name == "T_ARRAY") { 27 | $this->changeToState(CONVERTER_STATE_ARRAY); 28 | return true;//reprocess token 29 | } 30 | 31 | if ($name == "[") { 32 | 33 | $classScope = false; 34 | $this->stateMachine->startArrayScope("", true); 35 | // if ($this->stateMachine->currentScope instanceof ClassScope) { 36 | // $classScope = $this->stateMachine->currentScope; 37 | // } 38 | // 39 | // 40 | // $this->stateMachine->pushScope(CODE_SCOPE_ARRAY, $value, DECLARATION_TYPE_SQUARE_ARRAY); 41 | // 42 | // if ($classScope != false) { 43 | // $this->stateMachine->currentScope->setVariableName($classScope->currentVariableName); 44 | // } 45 | // 46 | // $this->changeToState(CONVERTER_STATE_DEFAULT); 47 | $this->stateMachine->currentTokenStream->insertToken('('); 48 | return false; 49 | } 50 | 51 | // if($this->stateMachine->currentScope instanceof ClassScope){ 52 | // throw new \Exception("Sorry, initializing class variables to an array is not supported yet. The difficultly is that the initial value must be moved from where it is declared to outside the class declaration code, which is difficult for arrays. Please instead declare the variable as null, and then assign it an array in the constructor."); 53 | // } 54 | 55 | throw new \Exception("The only symbols expected here are '=', ';' and some sort of value. Instead received token name [$name] with value [$value]."); 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeConverterState/define.php: -------------------------------------------------------------------------------- 1 | defineName = FALSE; 14 | $this->stateMachine->addJS("// "); 15 | $this->pastDefineToken = false; 16 | } 17 | 18 | function processToken($name, $value, $parsedToken) { 19 | 20 | $this->stateMachine->addJS($parsedToken); //Maybe should be parsedToken 21 | 22 | if($name == 'T_CONSTANT_ENCAPSED_STRING') { 23 | if($this->defineName == FALSE){ 24 | $this->defineName = unencapseString($value); 25 | } 26 | else{ 27 | $this->stateMachine->addDefine($this->defineName, unencapseString($value)); 28 | $this->changeToState(CONVERTER_STATE_DEFAULT); 29 | } 30 | } 31 | else if($name == 'T_LNUMBER') { 32 | $this->stateMachine->addDefine($this->defineName, intval($value, 0)); 33 | $this->changeToState(CONVERTER_STATE_DEFAULT); 34 | } 35 | else if($name == 'T_DNUMBER') { 36 | $this->stateMachine->addDefine($this->defineName, floatval($value)); 37 | $this->changeToState(CONVERTER_STATE_DEFAULT); 38 | } 39 | else if($name == 'T_STRING') { 40 | $this->stateMachine->addDefine($this->defineName, convertPHPValueToJSValue($value)); 41 | if ($this->pastDefineToken == true) { 42 | $this->changeToState(CONVERTER_STATE_DEFAULT); 43 | } 44 | } 45 | 46 | $this->pastDefineToken = true; 47 | } 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/PHPToJavascript/CodeScope.php: -------------------------------------------------------------------------------- 1 | jsElements[] = $scope; 28 | } 29 | 30 | function addJS($jsString){ 31 | $this->jsElements[] = $jsString; 32 | } 33 | 34 | function getJS(){ 35 | $js = ""; 36 | foreach($this->jsElements as $jsElement){ 37 | if($jsElement instanceof CodeScope){ 38 | $js .= $jsElement->getJS(); 39 | } 40 | else if(is_string($jsElement)){ 41 | $js .= $jsElement; 42 | } 43 | } 44 | 45 | return $js; 46 | } 47 | 48 | function markMethodsStart(){ 49 | throw new \Exception("This should only be called on ClassScope"); 50 | } 51 | 52 | 53 | 54 | /** 55 | * @param $variableName 56 | * @param $variableFlags 57 | * @return mixed 58 | * 59 | * For a given variable name, try to find the variable in the current scope. 60 | */ 61 | abstract function getScopedVariableForScope($variableName, $variableFlags); 62 | abstract function getType(); 63 | 64 | function getVariable($variableName){ 65 | if (array_key_exists($variableName, $this->scopedVariables)) { 66 | return $this->scopedVariables[$variableName]; 67 | } 68 | return null; 69 | } 70 | 71 | 72 | 73 | function getScopedVariable($variableName, $variableFlags, $originalScope){ 74 | 75 | $result = $this->getScopedVariableForScope($variableName, $variableFlags); 76 | 77 | if($result == NULL){ 78 | if($this->parentScope != NULL){ 79 | $result = $this->parentScope->getScopedVariable($variableName, $variableFlags, FALSE); 80 | } 81 | } 82 | 83 | if($originalScope == TRUE){ 84 | if($result == FALSE) { 85 | if(($variableFlags & DECLARATION_TYPE_CLASS) == 0){ 86 | //First use of variable in a function - lets add a 'var' to make Javascript happy. 87 | $this->addScopedVariable($variableName, $variableFlags); 88 | $result = "var $variableName"; 89 | } 90 | else{ 91 | //The variable really ought to exist in the class scope 92 | //But maybe it's been declared after it's use or is a SomeClass::param 93 | $result = $variableName; 94 | } 95 | } 96 | } 97 | 98 | return $result; 99 | } 100 | 101 | 102 | function getVariableFromScopeInternal($variableName) { 103 | if (array_key_exists($variableName, $this->scopedVariables)) { 104 | return $this->scopedVariables[$variableName]; 105 | } 106 | 107 | return null; 108 | } 109 | 110 | 111 | function getVariableFromScope($variableName) { 112 | 113 | $result = $this->getVariableFromScopeInternal($variableName); 114 | 115 | if($result == NULL){ 116 | if($this->parentScope != NULL){ 117 | $result = $this->parentScope->getVariableFromScope($variableName); 118 | } 119 | } 120 | 121 | return $result; 122 | } 123 | 124 | 125 | function getName(){ 126 | return $this->name; 127 | } 128 | 129 | function __construct($name, $parentScope){ 130 | $this->name = $name; 131 | $this->parentScope = $parentScope; 132 | } 133 | 134 | function pushBracket(){ 135 | $this->bracketCount += 1; 136 | } 137 | 138 | function popBracket(){ 139 | $this->bracketCount -= 1; 140 | if($this->bracketCount <= 0){ 141 | return TRUE; 142 | } 143 | 144 | return FALSE; 145 | } 146 | 147 | function pushParens(){ 148 | //does nothing 149 | } 150 | 151 | function popParens(){ 152 | //Does nothings 153 | return FALSE; 154 | } 155 | 156 | /** 157 | * Adds a variable to the scope 158 | * 159 | * @param $variableName 160 | * @param $variableFlag 161 | * @return bool true if it was a new variable to this scope. 162 | */ 163 | function addScopedVariable($variableName, $variableFlag){ 164 | 165 | if($variableFlag & DECLARATION_TYPE_CLASS) { 166 | if (!($variableFlag & DECLARATION_TYPE_PRIVATE)) { 167 | //In cases like "$this->variableName" variableName is never 168 | //added to the scope. 169 | return false; 170 | } 171 | } 172 | 173 | $cVar = cvar($variableName); 174 | 175 | if(PHPToJavascript::$TRACE == TRUE){ 176 | echo "Added variable $variableName to scope ".get_class($this)." with flag $variableFlag\n"; 177 | } 178 | 179 | if(array_key_exists($cVar, $this->scopedVariables) == FALSE){ 180 | $variable = new Variable($cVar, $variableFlag); 181 | $this->scopedVariables[$cVar] = $variable; 182 | return TRUE; 183 | } 184 | return FALSE; 185 | } 186 | 187 | 188 | 189 | function getVariablesWithDefaultParameters(){ 190 | return $this->defaultValues; 191 | } 192 | 193 | function startOfFunction(){ 194 | return FALSE; 195 | } 196 | 197 | function addStaticVariable($variableName){ 198 | throw new \Exception("This should only be called on ClassScope"); 199 | //Yes, I know this is terrible OO-ness. 200 | } 201 | 202 | function addPublicVariable($variableName){ 203 | throw new \Exception("This should only be called on ClassScope"); 204 | //Yes, I know this is terrible OO-ness. 205 | } 206 | 207 | function addToVariableValue($value){ 208 | throw new \Exception("This should only be called on ClassScope"); 209 | //Yes, I know this is terrible OO-ness. 210 | } 211 | 212 | function addParent($value){ 213 | throw new \Exception("This should only be called on ClassScope"); 214 | //Yes, I know this is terrible OO-ness. 215 | } 216 | 217 | 218 | 219 | /** 220 | * For class variables that are added to the class scope, but are delayed to be delcared outside 221 | * the function (to be public or static) we need to grab the default values to be able to set 222 | 223 | 224 | /** 225 | * Get the JS code that needs to be moved to after the end of this scope 226 | * @return string 227 | */ 228 | function getEndOfScopeJS(){ 229 | return ""; 230 | } 231 | 232 | // function getJSRaw(){ 233 | // $js = ""; 234 | // $js .= $this->getJS_InPlace(); 235 | // $js .= "\n"; 236 | // $js .= $this->getEndOfScopeJS(); 237 | // $js .= "\n"; 238 | // $js .= $this->getChildDelayedJS(); 239 | // 240 | // 241 | // 242 | // 243 | // 244 | // return $js; 245 | // } 246 | 247 | function getChildDelayedJS(){ 248 | $js = ""; 249 | 250 | foreach($this->jsElements as $jsElement){ 251 | if($jsElement instanceof CodeScope){ 252 | $js .= $jsElement->getDelayedJS($this->getName()); 253 | $js .= "\n"; 254 | } 255 | } 256 | 257 | return $js; 258 | } 259 | 260 | function getDelayedJS($parentScopeName){ 261 | return ""; 262 | } 263 | 264 | function getInPlaceJS(){ 265 | return $this->getJS(); 266 | } 267 | 268 | 269 | //Contains hacks 270 | function preStateMagic($name, $value){ 271 | 272 | } 273 | 274 | //Contains hacks 275 | function postStateMagic($name, $value){ 276 | } 277 | 278 | function findAncestorScopeByType($type){ 279 | 280 | if($this->parentScope == null){ 281 | return null; 282 | } 283 | 284 | if($this->parentScope->getType() == $type){ 285 | return $this->parentScope; 286 | } 287 | 288 | return $this->parentScope->findAncestorScopeByType($type); 289 | } 290 | 291 | 292 | function addToJsForPreviousVariable($value) { 293 | throw new \Exception("This has no default implementation."); 294 | } 295 | 296 | function previousTokensMatch($tokens) { 297 | 298 | $tokens = array_reverse($tokens);//Tokens are matched from the end. 299 | 300 | $position = count($this->jsElements) - 1; 301 | 302 | foreach ($tokens as $token) { 303 | if (strcmp($token, $this->jsElements[$position]) != 0) { 304 | return false; 305 | } 306 | $position--; 307 | } 308 | return true; 309 | } 310 | 311 | function deleteTokens($tokenCountToDelete) { 312 | for ($x=0 ; $x<$tokenCountToDelete ; $x++) { 313 | $discardedToken = array_pop($this->jsElements); 314 | } 315 | } 316 | } 317 | 318 | -------------------------------------------------------------------------------- /src/PHPToJavascript/ConverterStateMachine.php: -------------------------------------------------------------------------------- 1 | pushScope(CODE_SCOPE_GLOBAL, 'GLOBAL'); 93 | 94 | $this->states[CONVERTER_STATE_DEFAULT] = new CodeConverterState_Default($this); 95 | $this->states[CONVERTER_STATE_ECHO] = new CodeConverterState_Echo($this); 96 | $this->states[CONVERTER_STATE_ARRAY] = new CodeConverterState_ARRAY($this); 97 | $this->states[CONVERTER_STATE_CLASS] = new CodeConverterState_CLASS($this); 98 | $this->states[CONVERTER_STATE_FUNCTION] = new CodeConverterState_FUNCTION($this); 99 | 100 | $this->states[CONVERTER_STATE_FOREACH] = new CodeConverterState_TFOREACH($this); 101 | $this->states[CONVERTER_STATE_PUBLIC] = new CodeConverterState_TPUBLIC($this); 102 | $this->states[CONVERTER_STATE_VARIABLE] = new CodeConverterState_TVARIABLE($this); 103 | 104 | $this->states[CONVERTER_STATE_VARIABLE_GLOBAL] = new CodeConverterState_TVARIABLEGLOBAL($this); 105 | $this->states[CONVERTER_STATE_VARIABLE_FUNCTION] = new CodeConverterState_TVARIABLEFUNCTION($this); 106 | $this->states[CONVERTER_STATE_VARIABLE_CLASS] = new CodeConverterState_TVARIABLECLASS($this); 107 | 108 | $this->states[CONVERTER_STATE_VARIABLE_FUNCTION_PARAMETER] = new CodeConverterState_TVARIABLEPARAMETER($this); 109 | $this->states[CONVERTER_STATE_VARIABLE_ARRAY] = new CodeConverterState_TVARIABLEARRAY($this); 110 | 111 | 112 | $this->states[CONVERTER_STATE_STATIC] = new CodeConverterState_TSTATIC($this); 113 | $this->states[CONVERTER_STATE_STRING] = new CodeConverterState_TSTRING($this); 114 | 115 | $this->states[CONVERTER_STATE_T_PUBLIC] = new CodeConverterState_TPUBLIC($this); 116 | $this->states[CONVERTER_STATE_T_PRIVATE] = new CodeConverterState_TPRIVATE($this); 117 | 118 | $this->states[CONVERTER_STATE_DEFINE] = new CodeConverterState_define($this); 119 | 120 | $this->states[CONVERTER_STATE_T_EXTENDS] = new CodeConverterState_TEXTENDS($this); 121 | $this->states[CONVERTER_STATE_T_NEW] = new CodeConverterState_TNEW($this); 122 | 123 | $this->states[CONVERTER_STATE_VARIABLE_DEFAULT] = new CodeConverterState_VariableDefault($this); 124 | $this->states[CONVERTER_STATE_EQUALS] = new CodeConverterState_Equals($this); 125 | 126 | $this->states[CONVERTER_STATE_CLOSE_PARENS] = new CodeConverterState_CLOSEPARENS($this); 127 | $this->states[CONVERTER_STATE_COMMA] = new CodeConverterState_Comma($this); 128 | $this->states[CONVERTER_STATE_DOUBLE_ARROW] = new CodeConverterState_DoubleArrow($this); 129 | 130 | 131 | 132 | $this->states[CONVERTER_STATE_IMPLEMENTS_INTERFACE] = new CodeConverterState_TIMPLEMENTSINTERFACE($this); 133 | $this->states[CONVERTER_STATE_INTERFACE] = new CodeConverterState_TINTERFACE($this); 134 | 135 | 136 | $this->states[CONVERTER_STATE_REQUIRE] = new CodeConverterState_REQUIRE($this); 137 | $this->states[CONVERTER_STATE_ABSTRACT] = new CodeConverterState_TABSTRACT($this); 138 | $this->states[CONVERTER_STATE_ABSTRACT_FUNCTION] = new CodeConverterState_TABSTRACTREMOVE($this); 139 | 140 | $this->states[CONVERTER_STATE_END_OF_CLASS] = new CodeConverterState_EndOfClass($this); 141 | $this->states[CONVERTER_STATE_VARIABLE_VALUE] = new CodeConverterState_VariableValue($this); 142 | 143 | $this->states[CONVERTER_STATE_OBJECT_OPERATOR] = new CodeConverterState_TOBJECTOPERATOR($this); 144 | 145 | $this->states[CONVERTER_STATE_DOUBLE_COLON] = new CodeConverterState_TDOUBLECOLON($this); 146 | $this->states[CONVERTER_STATE_NAME_SPACE] = new CodeConverterState_TNAMESPACE($this); 147 | $this->states[CONVERTER_STATE_IMPORT_NAMESPACE] = new CodeConverterState_ImportNamespace($this); 148 | $this->states[CONVERTER_STATE_T_USE] = new CodeConverterState_TUSE($this); 149 | 150 | 151 | $this->states[CONVERTER_STATE_T_UNSET] = new CodeConverterState_TUNSET($this); 152 | 153 | $this->states[CONVERTER_STATE_T_TRY] = new CodeConverterState_TTRY($this); 154 | $this->states[CONVERTER_STATE_T_CATCH] = new CodeConverterState_TCATCH($this); 155 | 156 | 157 | $this->states[CONVERTER_STATE_VARIABLE_CATCH] = new CodeConverterState_TVARIABLECATCH($this); 158 | 159 | $this->states[CONVERTER_STATE_GLOBAL] = new CodeConverterState_TGLOBAL($this); 160 | $this->states[CONVERTER_STATE_SKIP_TO_SEMICOLON] = new CodeConverterState_SkipToSemiColon($this); 161 | 162 | $this->states[CONVERTER_STATE_CAPTURING_DEFAULT_VALUE] = new CodeConverterState_CapturingDefaultValue($this); 163 | $this->states[CONVERTER_STATE_EMBEDDED_VARIABLE] = new CodeConverterState_EmbeddedVariable($this); 164 | 165 | 166 | $this->currentState = CONVERTER_STATE_DEFAULT; 167 | } 168 | 169 | 170 | function getPreviousNonWhitespaceToken(&$name, &$value){ 171 | return $this->currentTokenStream->getPreviousNonWhitespaceToken($name, $value); 172 | } 173 | 174 | function previousTokensMatch($tokens) { 175 | return $this->currentScope->previousTokensMatch($tokens); 176 | } 177 | 178 | function deleteTokens($tokenCountToDelete) { 179 | $this->currentScope->deleteTokens($tokenCountToDelete); 180 | } 181 | 182 | 183 | 184 | /** 185 | * Adds a variable to the current scope. 186 | * @param $variableName 187 | * @param $variableFlags 188 | * @return bool Whether the variable was a new one to the current scope. 189 | */ 190 | function addScopedVariable($variableName, $variableFlags){ 191 | return $this->currentScope->addScopedVariable($variableName, $variableFlags); 192 | } 193 | 194 | function getVariableNameForScope($variableName, $variableFlags){ 195 | return $this->currentScope->getScopedVariable($variableName, $variableFlags, true); 196 | } 197 | 198 | function getVariableFromScopes($variableName) { 199 | return $this->currentScope->getVariableFromScope($variableName); 200 | } 201 | 202 | function findScopeType($type){ 203 | foreach($this->scopesStack as $scope){ 204 | //TODO How to convert this bad code into 205 | // $scope instanceof $variable - where $variable == classname 206 | if($scope->getType() == $type){ 207 | return $scope; 208 | } 209 | } 210 | 211 | return null; 212 | } 213 | 214 | function getJS(){ 215 | return $this->rootScope->getJS(); 216 | } 217 | 218 | public function addJS($jsString){ 219 | if(PHPToJavascript::$TRACE){ 220 | echo "$jsString \n"; 221 | } 222 | $this->currentScope->addJS($jsString); 223 | } 224 | 225 | function changeToState($newState, $extraParams = array()){ 226 | if(array_key_exists($newState, $this->states) == false){ 227 | throw new \Exception("Unknown state [$newState], cannot changeState to it."); 228 | } 229 | 230 | $this->currentState = $newState; 231 | $this->states[$this->currentState]->enterState($extraParams); 232 | } 233 | 234 | function clearVariableFlags(){ 235 | $this->variableFlags = false; 236 | } 237 | 238 | function addVariableFlags($variableFlag){ 239 | $this->variableFlags |= $variableFlag; 240 | } 241 | 242 | /** 243 | * @param $name 244 | * @param $value 245 | * @param $parsedToken 246 | * @return bool true if the token should be reprocessed, e.g. because of a change of state. 247 | */ 248 | function processToken($name, $value, $parsedToken){ 249 | if(PHPToJavascript::$TRACE == true){ 250 | echo "SM ".get_class($this->getState())." token [$name] => [$value]".NL; 251 | } 252 | return $this->getState()->processToken($name, $value, $parsedToken); 253 | } 254 | 255 | function getState(){ 256 | return $this->states[$this->currentState]; 257 | } 258 | 259 | function accountForOpenBrackets($name){ 260 | if($name == "{"){ 261 | $this->currentScope->pushBracket(); 262 | } 263 | if($name == "("){ 264 | $this->currentScope->pushParens(); 265 | } 266 | } 267 | 268 | function accountForQuotes($name){ 269 | if($name == '"' || $name == "'"){ 270 | if($this->quoteOpen == $name){ //Quote was open 271 | $this->quoteOpen = false; //now it's closed 272 | } 273 | else{ 274 | $this->quoteOpen = $name; 275 | } 276 | } 277 | } 278 | 279 | /** 280 | * Encloses a variable so that it can be used in a string properly e.g. 281 | * $target = "world"; 282 | * $greeting = "Hello $target!"; 283 | * 284 | * is converted to 285 | * var target = "world"; 286 | * var greeting = "Hello " + target +"!"; 287 | * 288 | * @param $variableName 289 | * @return string 290 | */ 291 | function encloseVariable($variableName){ 292 | if($this->quoteOpen == false){ 293 | return $variableName; 294 | } 295 | 296 | return $this->quoteOpen." + ".$variableName." + ".$this->quoteOpen; 297 | } 298 | 299 | 300 | function accountForCloseBrackets($name){ 301 | 302 | $scopeEnded = false; 303 | 304 | if($name == "}"){ 305 | $scopeEnded = $this->currentScope->popBracket(); 306 | } 307 | else if($name == ")"){ 308 | $scopeEnded = $this->currentScope->popParens(); 309 | } 310 | 311 | if ($scopeEnded == true){ 312 | if(($this->currentScope instanceof GlobalScope) == false){ 313 | $poppedScope = $this->currentScope; 314 | 315 | $this->popCurrentScope(); //It was the last bracket for a function. 316 | 317 | if($poppedScope instanceof FunctionScope){ 318 | $this->popCurrentScope();//Also pop the function paramters scope. 319 | } 320 | } 321 | } 322 | } 323 | 324 | function parseToken ($name, $value, $count) { 325 | 326 | $returnValue = $this->getPendingInsert($name); 327 | 328 | 329 | if($name == "T_VARIABLE"){ 330 | $returnValue .= $value; 331 | } 332 | else if (in_array($name, array_keys(self::$_convert))) { 333 | if(empty(self::$_convert[$name]) == true){ 334 | $returnValue .= $name; //keep key 335 | } 336 | else{ 337 | $returnValue .= self::$_convert[$name]; 338 | } 339 | } 340 | else if (in_array($name, self::$_keep)) { //keep value 341 | $returnValue .= $name; 342 | } 343 | else if($name == 'T_STRING' && defined($value)){ 344 | $returnValue .= constant($value); 345 | } 346 | else if (in_array($name, self::$_keepValue)) { 347 | $returnValue .= $value; 348 | } 349 | 350 | if($returnValue == 'NULL'){ 351 | $returnValue = 'null'; 352 | } 353 | 354 | return $returnValue; 355 | } 356 | 357 | 358 | function getPendingInsert($symbolToCheck){ 359 | foreach($this->pendingSymbols as $key => $pendingSymbol){ 360 | 361 | $symbol = $pendingSymbol[0]; 362 | $insert = $pendingSymbol[1]; 363 | 364 | if($symbolToCheck == $symbol /*|| 365 | $symbol == '*any*' */) { 366 | unset($this->pendingSymbols[$key]); 367 | return $insert; 368 | } 369 | } 370 | 371 | return ''; 372 | } 373 | 374 | //TODO - HACK HACK HACK 375 | var $insertToken = false; 376 | 377 | function addSymbolAfterNextToken($symbol){ 378 | $this->insertToken = $symbol; 379 | } 380 | 381 | function setPendingSymbol($symbol, $insert){ 382 | $this->pendingSymbols[] = array($symbol, $insert); 383 | } 384 | 385 | function getScopeType(){ 386 | return $this->currentScope->type; 387 | } 388 | 389 | /** 390 | * @param $variableName 391 | * @param $scopeType 392 | * @return null|Variable 393 | */ 394 | function getVariableFromScope($variableName, $scopeType) { 395 | $parentClassScope = $this->currentScope->findAncestorScopeByType($scopeType); 396 | 397 | if ($parentClassScope) { 398 | return $parentClassScope->getVariable($variableName); 399 | } 400 | 401 | return null; 402 | } 403 | 404 | 405 | function getScopeName(){ 406 | $parentClassScope = $this->currentScope->findAncestorScopeByType(CODE_SCOPE_CLASS); 407 | if($parentClassScope != null){ 408 | return "this.".$this->currentScope->getName(); 409 | } 410 | 411 | return $this->currentScope->getName(); 412 | } 413 | 414 | function pushScope($type, $name, $variableFlag = 0){ 415 | if($this->currentScope != null){ 416 | array_push($this->scopesStack, $this->currentScope); 417 | } 418 | 419 | switch($type){ 420 | case(CODE_SCOPE_GLOBAL):{ 421 | $newScope = new GlobalScope($name, $this->currentScope); 422 | break; 423 | } 424 | 425 | case(CODE_SCOPE_CLASS):{ 426 | $newScope = new ClassScope($name, $this->currentScope); 427 | break; 428 | } 429 | 430 | case(CODE_SCOPE_FUNCTION_PARAMETERS):{ 431 | $newScope = new FunctionParameterScope($name, $this->currentScope, $variableFlag); 432 | break; 433 | } 434 | 435 | case(CODE_SCOPE_FUNCTION):{ 436 | $newScope = new FunctionScope($name, $this->currentScope); 437 | break; 438 | } 439 | 440 | case(CODE_SCOPE_ARRAY):{ 441 | $newScope = new ArrayScope($name, $this->currentScope, $variableFlag); 442 | break; 443 | } 444 | 445 | case(CODE_SCOPE_CATCH):{ 446 | $newScope = new CatchScope($name, $this->currentScope); 447 | break; 448 | } 449 | 450 | default:{ 451 | throw new \Exception("Unknown scope type [".$type."]"); 452 | break; 453 | } 454 | } 455 | 456 | if($this->currentScope == null){ 457 | $this->rootScope = $newScope; 458 | } 459 | else{ 460 | $this->currentScope->addChild($newScope); 461 | } 462 | 463 | $this->currentScope = $newScope; 464 | 465 | if($type == CODE_SCOPE_CLASS){ 466 | $this->methodsStartIndex = 0; 467 | } 468 | } 469 | 470 | function resetVariableFlags() { 471 | //TODO - this is a good idea but may only work on popping scope. 472 | //the static and private var flag needs to operate across a function_parameter_scope into function_scope 473 | if ($this->variableFlags != 0) { 474 | echo "Warning: Variable flags is not zero but [" . $this->variableFlags . "] which probably means some flags were unused."; 475 | $this->variableFlags = 0; 476 | } 477 | } 478 | 479 | function popCurrentScope(){ 480 | 481 | //echo "popped scope ".$this->currentScope->getType()."\n"; 482 | 483 | //$this->resetVariableFlags(); 484 | 485 | $previousScope = $this->currentScope; 486 | 487 | $this->currentScope = array_pop($this->scopesStack); 488 | 489 | if($previousScope instanceof ClassScope){ 490 | $this->changeToState(CONVERTER_STATE_END_OF_CLASS, array('previousScope' => $previousScope)); 491 | } 492 | 493 | if($previousScope instanceof GlobalScope){ 494 | echo "We have left the global scope?"; 495 | } 496 | } 497 | 498 | 499 | function finalize(){ 500 | $code = $this->getJS(); 501 | return $code; 502 | } 503 | 504 | function addDefine($name, $value){ 505 | $this->defines[$name] = $value; 506 | } 507 | 508 | function isDefined($name){ 509 | return array_key_exists($name, $this->defines) == true; 510 | } 511 | 512 | function getDefine($name) { 513 | if(array_key_exists($name, $this->defines) == true){ 514 | return (string)$this->defines[$name]; 515 | } 516 | 517 | return null; 518 | } 519 | 520 | function getClassName(){ 521 | $scope = $this->findScopeType(CODE_SCOPE_CLASS); 522 | if($scope != null){ 523 | return $scope->name; 524 | } 525 | 526 | throw new \Exception("Trying to get class but no class scope found."); 527 | } 528 | 529 | function addDefaultsForVariables(){ 530 | $functionParametersScope = $this->findScopeType(CODE_SCOPE_FUNCTION_PARAMETERS); 531 | 532 | if($functionParametersScope == null){ 533 | // throw new \Exception("We're inside a function but we can't find the CODE_SCOPE_FUNCTION_PARAMETERS - that shouldn't be possible."); 534 | 535 | //We're probably inside a catch block 536 | return; 537 | } 538 | 539 | $variablesWithDefaultParameters = $functionParametersScope->getVariablesWithDefaultParameters(); 540 | 541 | foreach($variablesWithDefaultParameters as $variable => $default){ 542 | $jsString = "\n"; 543 | $jsString .= "\t\tif(typeof $variable === \"undefined\"){\n"; 544 | $jsString .= "\t\t\t$variable = $default;\n"; 545 | $jsString .= "\t\t}\n"; 546 | 547 | $this->addJS($jsString); 548 | } 549 | } 550 | 551 | public $requireFilename = null; 552 | 553 | function requireFile($requireFilename){ 554 | 555 | $requireFilename = str_replace('"', '', $requireFilename); 556 | $requireFilename = str_replace("'", '', $requireFilename); 557 | 558 | $this->requireFilename = $requireFilename; 559 | } 560 | 561 | function getRequiredFile(){ 562 | $value = $this->requireFilename; 563 | $this->requireFilename = null; 564 | return $value; 565 | } 566 | 567 | function scopePreStateMagic($name, $value){ 568 | $this->currentScope->preStateMagic($name, $value); 569 | } 570 | 571 | function scopePostStateMagic($name, $value){ 572 | $this->currentScope->postStateMagic($name, $value); 573 | } 574 | 575 | 576 | function startArrayScope($scopeName, $startedBySquareBracket) { 577 | 578 | $classScope = false; 579 | 580 | if ($this->currentScope instanceof ClassScope) { 581 | $classScope = $this->currentScope; 582 | } 583 | 584 | $this->pushScope(CODE_SCOPE_ARRAY, $scopeName); 585 | $this->changeToState(CONVERTER_STATE_DEFAULT); 586 | 587 | if ($classScope != false) { 588 | $this->currentScope->setVariableName($classScope->currentVariableName); 589 | } 590 | 591 | if ($startedBySquareBracket == true) { 592 | $this->currentScope->incrementSquareBracketCount(); 593 | } 594 | 595 | $this->currentScope->startedBySquareBracket = $startedBySquareBracket; 596 | } 597 | 598 | 599 | 600 | /** @var array these token keys will be converted to their values */ 601 | public static $_convert = array ( 602 | 'T_IS_EQUAL'=>'==', 603 | 'T_IS_GREATER_OR_EQUAL'=>'>=', 604 | 'T_IS_SMALLER_OR_EQUAL'=>'<=', 605 | 'T_IS_IDENTICAL'=>'===', 606 | 'T_IS_NOT_EQUAL'=>'!=', 607 | 'T_IS_NOT_IDENTICAL'=>'!==', 608 | 'T_IS_SMALLER_OR_EQUA'=>'<=', 609 | 'T_BOOLEAN_AND'=>'&&', 610 | 'T_BOOLEAN_OR'=>'||', 611 | 'T_CONCAT_EQUAL'=>'+= ', 612 | 'T_DIV_EQUAL'=>'/=', 613 | //'T_DOUBLE_COLON'=>'.', 614 | 'T_INC'=>'++', 615 | 'T_DEC'=>'--', 616 | 'T_MINUS_EQUAL'=>'-=', 617 | 'T_MOD_EQUAL'=>'%=', 618 | 'T_MUL_EQUAL'=>'*=', 619 | 'T_OBJECT_OPERATOR'=>'.', 620 | 'T_OR_EQUAL'=>'|=', 621 | 'T_PLUS_EQUAL'=>'+=', 622 | 'T_SL'=>'<<', 623 | 'T_SL_EQUAL'=>'<<=', 624 | 'T_SR'=>'>>', 625 | 'T_SR_EQUAL'=>'>>=', 626 | 'T_START_HEREDOC'=>'<<<', 627 | 'T_XOR_EQUAL'=>'^=', 628 | //'T_NEW'=>'new', 629 | 'T_ELSE'=>'else', 630 | '.'=>' + "" + ', 631 | 'T_IF'=>'if', 632 | 'T_RETURN'=>'return', 633 | 'T_AS'=>'in', 634 | 'T_WHILE'=>'while', 635 | 'T_LOGICAL_AND' => 'AND', 636 | 'T_LOGICAL_OR' => 'OR', 637 | 'T_LOGICAL_XOR' => 'XOR', 638 | 'T_EVAL' => 'eval', 639 | 'T_ELSEIF' => 'else if', 640 | 'T_BREAK' => 'break', 641 | 'T_INSTANCEOF' => 'instanceof' 642 | ); 643 | 644 | /** @var array these tokens stays the same */ 645 | public static $_keep = array( 646 | //'=', 647 | //',', //Replaced by comma state 648 | '}', '{', 649 | ';', 650 | '(', //')', 651 | '*', 652 | '/', '+', '-', '>', 653 | '<', '[', ']', "\"", 654 | "'", ":", 655 | '%', '?', '!' 656 | ); 657 | 658 | /** @var array these tokens keeps their value */ 659 | public static $_keepValue = array ( 660 | 'T_STRING', 661 | 'T_COMMENT', 662 | 'T_ML_COMMENT', 663 | 'T_DOC_COMMENT', 664 | 'T_LNUMBER', 665 | 'T_ENCAPSED_AND_WHITESPACE', 666 | 'T_WHITESPACE', 667 | 'T_SWITCH', 668 | 'T_CASE', 669 | 'T_DEFAULT', 670 | 'T_THROW', 671 | 'T_FOR', 672 | 'T_CONTINUE', 673 | 'T_DNUMBER' 674 | ); 675 | 676 | } 677 | 678 | 679 | -------------------------------------------------------------------------------- /src/PHPToJavascript/FunctionParameterScope.php: -------------------------------------------------------------------------------- 1 | variableFlag = $variableFlag; 30 | } 31 | 32 | function getInPlaceJS(){ 33 | if($this->getName() == "__construct"){ 34 | //constructor gets included in ClassScope 35 | return ""; 36 | } 37 | 38 | if(($this->variableFlag & DECLARATION_TYPE_PRIVATE) == 0){ 39 | $jsRaw = $this->getJS(); 40 | 41 | if(($this->variableFlag & DECLARATION_TYPE_STATIC)){ 42 | //$jsRaw = str_replace(PUBLIC_FUNCTION_MARKER_MAGIC_STRING, $parentScopeName.".", $jsRaw); 43 | } 44 | else{ 45 | $jsRaw = str_replace(PUBLIC_FUNCTION_MARKER_MAGIC_STRING, "this.", $jsRaw); 46 | 47 | //Functions declared as prototypes on the class object need to have a semi-colon 48 | //to avoid a missing ';' warning in jsLint 49 | $jsRaw = trim($jsRaw).";\n\n"; 50 | return $jsRaw; 51 | } 52 | } 53 | else{ 54 | //$result = "this.".$this->getName()." = "; 55 | $result = $this->getJS(); 56 | //$jsRaw = str_replace_count($this->getName(), '/*'.$this->getName().'*/', $jsRaw, 1); 57 | //$result .= $jsRaw; 58 | return $result; 59 | } 60 | } 61 | 62 | function getDelayedJS($parentScopeName){ 63 | 64 | if($this->getName() == "__construct"){ 65 | //constructor gets included in ClassScope 66 | return ""; 67 | } 68 | 69 | if(($this->variableFlag & DECLARATION_TYPE_PRIVATE) == 0){ 70 | $jsRaw = $this->getJS(); 71 | 72 | if(($this->variableFlag & DECLARATION_TYPE_STATIC)){ 73 | $jsRaw = str_replace(PUBLIC_FUNCTION_MARKER_MAGIC_STRING, $parentScopeName.".", $jsRaw); 74 | return $jsRaw; 75 | } 76 | else{ 77 | 78 | } 79 | } 80 | return ""; 81 | } 82 | 83 | function getType(){ 84 | return CODE_SCOPE_FUNCTION_PARAMETERS; 85 | } 86 | 87 | function getScopedVariableForScope($variableName, $variableFlags){ 88 | $cVar = cvar($variableName); 89 | 90 | if(array_key_exists($cVar, $this->scopedVariables) == TRUE){ 91 | //$variableFlag = $this->scopedVariables[$cVar]; 92 | $variable = $this->scopedVariables[$cVar]; 93 | 94 | //if($variableFlag & DECLARATION_TYPE_STATIC){ 95 | if ($variable->isStatic() == true) { 96 | return $this->name.".".$variableName; 97 | } 98 | else if ($variableFlags & DECLARATION_TYPE_CLASS) { 99 | return 'this.'.$variableName; 100 | } 101 | 102 | return $variableName; 103 | } 104 | 105 | return NULL; 106 | } 107 | 108 | function addToJsForPreviousVariable($value) { 109 | 110 | if($this->beforeVariable == TRUE){ 111 | //It's actually a type-hint not a default value, as it before the variable name 112 | return; 113 | } 114 | 115 | $allKeys = array_keys($this->scopedVariables); 116 | if(count($allKeys) == 0){ 117 | throw new \Exception("Trying to add default variable but not variables found yet."); 118 | } 119 | 120 | $variableName = $allKeys[count($allKeys) - 1]; 121 | 122 | if (isset($this->defaultValues[$variableName]) == false) { 123 | $this->defaultValues[$variableName] = ''; 124 | } 125 | 126 | $this->defaultValues[$variableName] .= convertPHPValueToJSValue($value); 127 | } 128 | 129 | /** 130 | * @param $variableName 131 | * @param $variableFlag 132 | * @return bool 133 | */ 134 | function addScopedVariable($variableName, $variableFlag){ 135 | $result = parent::addScopedVariable($variableName, $variableFlag); 136 | $this->setBeforeVariable(FALSE); 137 | return $result; 138 | } 139 | 140 | function setBeforeVariable($boolean){ 141 | $this->beforeVariable = $boolean; 142 | } 143 | } 144 | 145 | 146 | -------------------------------------------------------------------------------- /src/PHPToJavascript/FunctionScope.php: -------------------------------------------------------------------------------- 1 | bracketCount == 1){//And we're past the first opening bracket 13 | return TRUE; 14 | } 15 | return FALSE; 16 | } 17 | 18 | function getScopedName(){ 19 | $containingClassScope = $this->findAncestorScopeByType(CODE_SCOPE_CLASS); 20 | 21 | if($containingClassScope == null){ 22 | return $this->name; 23 | } 24 | else{ 25 | return "this.".$this->name; 26 | } 27 | } 28 | 29 | 30 | function getScopedVariableForScope($variableName, $variableFlags){ 31 | $cVar = cvar($variableName); 32 | 33 | if(array_key_exists($cVar, $this->scopedVariables) == TRUE){ 34 | //$variableFlag = $this->scopedVariables[$cVar]; 35 | $variable = $this->scopedVariables[$cVar]; 36 | //if($variableFlag & DECLARATION_TYPE_STATIC){ 37 | if ($variable->isStatic() == true) { 38 | return $this->getScopedName().".".$variableName; 39 | } 40 | else if($variableFlags & DECLARATION_TYPE_CLASS){ 41 | if(strpos($variableName, "$") !== FALSE){ 42 | //it's a variable variable like "this->$var"; 43 | return 'this['.$variableName.']'; 44 | } 45 | else{ 46 | return 'this.'.$variableName; 47 | } 48 | } 49 | 50 | return $variableName; 51 | } 52 | 53 | return NULL; 54 | } 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/PHPToJavascript/GlobalScope.php: -------------------------------------------------------------------------------- 1 | scopedVariables) == TRUE){ 24 | return $variableName; 25 | } 26 | 27 | return NULL; 28 | } 29 | } 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/PHPToJavascript/PHPToJavascript.php: -------------------------------------------------------------------------------- 1 | stateMachine = new ConverterStateMachine(); 177 | 178 | //TODO - figure out what to do with PHP constants and the PHP/JS 'standard' library 179 | //which apparently converts the constants to strings. 180 | $this->addPostConversionReplace('STR_PAD_LEFT', "'STR_PAD_LEFT'"); 181 | 182 | //Javascript has a magic operator '+' which forces objects to a 'number' value 183 | // e.g. value = '123'; 184 | // value + 123 => '123123' 185 | // but (+value + 123) = 245; 186 | // It casts to float/int as appropriate. 187 | //TODO figure out how to expose this in not such hacky fashion. 188 | $this->addPostConversionReplace('/*value*/', "+"); 189 | } 190 | 191 | function addFromFile($filename){ 192 | $code = file_get_contents($filename); 193 | if($code === FALSE){ 194 | throw new \Exception("Could not open $filename."); 195 | } 196 | 197 | $tokenStream = new TokenStream($code); 198 | processTokenStream($tokenStream, $this->stateMachine, NULL); 199 | } 200 | 201 | function addFromString($code){ 202 | $tokenStream = new TokenStream($code); 203 | processTokenStream($tokenStream, $this->stateMachine, NULL); 204 | } 205 | 206 | function setTrace($boolean){ 207 | self::$TRACE = $boolean; 208 | } 209 | 210 | /** 211 | * Set what echo function in PHP is converted to. The trailing bracket "(" on the function is required. 212 | * 213 | * @param $echoConversionFunction. This should take the form of "callableJavascriptFunction(" 214 | * 215 | * TODO - should support callback function here to allow context sensitive replacement. 216 | */ 217 | function setEchoConversionFunction($echoConversionFunction){ 218 | CodeConverterState_Echo::setEchoConversionFunction($echoConversionFunction); 219 | } 220 | 221 | 222 | function toJavascript(){ 223 | $output = $this->stateMachine->finalize(); 224 | 225 | $searchArray = array_keys($this->postConversionReplacements); 226 | $replaceArray = array_values($this->postConversionReplacements); 227 | 228 | $output = str_replace($searchArray, $replaceArray, $output); 229 | 230 | return $output; 231 | } 232 | 233 | function addPostConversionReplace($search, $replace){ 234 | $this->postConversionReplacements[$search] = $replace; 235 | } 236 | 237 | function generateFile($outputFilename, $originalFilename) { 238 | 239 | $jsOutput = $this->toJavascript(); 240 | 241 | $outputDirectory = pathinfo($outputFilename, PATHINFO_DIRNAME); 242 | 243 | $this->ensureDirectoryExists($outputDirectory); 244 | 245 | $fileHandle = fopen($outputFilename, "w"); 246 | 247 | if ($fileHandle == FALSE) { 248 | throw new \Exception("Failed to open file [$outputFilename] for writing."); 249 | } 250 | 251 | fwrite($fileHandle, "//Auto-generated file by PHP-To-Javascript at ".date(DATE_RFC822).NL); 252 | fwrite($fileHandle, "//\n"); 253 | fwrite($fileHandle, "//DO NOT EDIT - all changes will be lost.\n"); 254 | fwrite($fileHandle, "//\n"); 255 | fwrite($fileHandle, "//Please edit the file " . $originalFilename . " and then reconvert to make any changes\n"); 256 | fwrite($fileHandle, "\n"); 257 | 258 | fwrite($fileHandle, $jsOutput); 259 | 260 | fclose($fileHandle); 261 | } 262 | 263 | 264 | function ensureDirectoryExists($filePath) { 265 | 266 | $pathSegments = array(); 267 | 268 | $slashPosition = 0; 269 | $finished = FALSE; 270 | 271 | while ($finished === FALSE) { 272 | $slashPosition = strpos($filePath, '/', $slashPosition + 1); 273 | if ($slashPosition === FALSE) { 274 | $finished = TRUE; 275 | } else { 276 | $pathSegments[] = substr($filePath, 0, $slashPosition); 277 | } 278 | 279 | if (count($pathSegments) > 10) { 280 | $finished = FALSE; 281 | } 282 | } 283 | 284 | foreach ($pathSegments as $segment) { 285 | if (file_exists($segment) === FALSE) { 286 | $directoryCreated = mkdir($segment); 287 | 288 | if ($directoryCreated == FALSE) { 289 | throw new \Exception("Failed to create directory $filePath"); 290 | } 291 | } 292 | } 293 | } 294 | } 295 | 296 | 297 | function processTokenStream(TokenStream $tokenStream, ConverterStateMachine $stateMachine, $originalFilename){ 298 | 299 | $name = ''; 300 | $value = ''; 301 | 302 | $stateMachine->currentTokenStream = $tokenStream; 303 | 304 | while($tokenStream->hasMoreTokens() == TRUE){ 305 | $tokenStream->next($name, $value); 306 | 307 | $count = 0; 308 | 309 | $parsedToken = $stateMachine->parseToken($name, $value, $count); 310 | 311 | //TODO - both of these should be somewhere more logical. 312 | if ($name == 'T_CONSTANT_ENCAPSED_STRING'){ 313 | $value = convertMultiLineString($value); 314 | } 315 | 316 | if ($name == 'T_ENCAPSED_AND_WHITESPACE') { 317 | $parsedToken = convertMultiLineString($parsedToken); 318 | } 319 | 320 | $stateMachine->accountForOpenBrackets($name); 321 | $stateMachine->accountForQuotes($name); 322 | $stateMachine->scopePreStateMagic($name, $value); 323 | 324 | do{ 325 | $reprocess = $stateMachine->processToken($name, $value, $parsedToken); 326 | 327 | if($count > 5){ 328 | throw new \Exception("Stuck converting same token."); 329 | } 330 | 331 | $count++; 332 | } 333 | while($reprocess == TRUE); 334 | 335 | $stateMachine->accountForCloseBrackets($name); 336 | $stateMachine->scopePostStateMagic($name, $value); 337 | 338 | 339 | 340 | 341 | if($name == 'T_VARIABLE'){ 342 | //If there's a token that needs to be inserted e.g. 'var' 343 | if($stateMachine->insertToken != FALSE){ 344 | $stateMachine->addJS($stateMachine->insertToken); 345 | $stateMachine->insertToken = FALSE; 346 | } 347 | } 348 | 349 | if(FALSE){ 350 | $requiredFile = $stateMachine->getRequiredFile(); 351 | if($requiredFile != NULL){ 352 | 353 | //echo "Figure out where $requiredFile is from original file path $originalFilename"; 354 | //TraitInclude.php' is from original file path TraitExample.php 355 | 356 | $pathParts = pathinfo($originalFilename); 357 | 358 | $requireFilePath = $pathParts['dirname'].'/'.$requiredFile; 359 | 360 | //$requireFilePath = realpath($requireFilePath); 361 | 362 | if(PHPToJavascript::$TRACE == TRUE){ 363 | echo "Including file [$requiredFile] on path [$requireFilePath]."; 364 | } 365 | 366 | $code = file_get_contents($requireFilePath); 367 | 368 | if($code === FALSE){ 369 | throw new \Exception("Could not open file [$requiredFile] on path [$requireFilePath]."); 370 | } 371 | 372 | $requireTokenStream = new TokenStream($code); 373 | 374 | processTokenStream($requireTokenStream, $stateMachine, $originalFilename); 375 | $stateMachine->addJS("\n//End of require\n"); 376 | //TODO Add a new state to tidy up semi-colon after include 377 | } 378 | } 379 | } 380 | } 381 | 382 | 383 | /** 384 | * I hate PHPs 'feature' of not throwing an error when you set a variable for a class that doesn't exist. 385 | * e.g. I meant to type: 386 | * $this->isValidated = true; 387 | * but accidentally type 388 | * $this->isVlidated = true; 389 | * 390 | * As you spend an hour trying to find out why isValidated isn't being set. This trait turns all bad 391 | * get and set calls on non-existent variables into exceptions. 392 | */ 393 | trait SafeAccess { 394 | public function __set($name, $value) { 395 | throw new \Exception("Property [$name] doesn't exist for class [".__CLASS__."] so can set it"); 396 | } 397 | public function __get($name) { 398 | throw new \Exception("Property [$name] doesn't exist for class [".__CLASS__."] so can get it"); 399 | } 400 | } 401 | 402 | -------------------------------------------------------------------------------- /src/PHPToJavascript/TokenStream.php: -------------------------------------------------------------------------------- 1 | tokens = token_get_all($code); 17 | //$this->count = count($this->tokens); 18 | } 19 | 20 | function hasMoreTokens(){ 21 | if ($this->current < count($this->tokens)){ 22 | return TRUE; 23 | } 24 | 25 | return FALSE; 26 | } 27 | 28 | function getTokenAtIndex($index, &$name, &$value) { 29 | $token = $this->tokens[$index]; 30 | 31 | if (is_array($token)) { 32 | $name = trim(token_name($token[0])); 33 | $value = $token[1]; 34 | } 35 | else { 36 | $name = trim($token); 37 | $value = ''; 38 | } 39 | } 40 | 41 | function next(&$name, &$value) { 42 | $this->getTokenAtIndex($this->current, $name, $value); 43 | $this->current++; 44 | } 45 | 46 | function getCurrentIndex(){ 47 | return $this->current; 48 | } 49 | 50 | function insertToken($name, $value = false) { 51 | $token = array( 52 | $name, 53 | $value, 54 | ); 55 | 56 | array_splice($this->tokens, $this->current, 0, array($token)); 57 | 58 | if ($value === false) { 59 | $this->tokens[$this->current] = $name; 60 | } 61 | } 62 | 63 | 64 | function getPreviousNonWhitespaceToken(&$name, &$value) { 65 | $indexToReturn = $this->current - 2; //-1 gets back to the current token, -2 is the previous token. 66 | 67 | do{ 68 | $this->getTokenAtIndex($indexToReturn, $name, $value); 69 | if ($name == 'T_COMMENT' || 70 | $name == 'T_WHITESPACE') { 71 | //Keep going 72 | } 73 | else{ 74 | //We are done. 75 | return; 76 | } 77 | $indexToReturn -= 1; 78 | } while($indexToReturn >= 0); 79 | 80 | $name = null; //Could throw an exception? 81 | $value = null; 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /src/PHPToJavascript/Variable.php: -------------------------------------------------------------------------------- 1 | name = $name; 15 | $this->flags = $flags; 16 | } 17 | 18 | public function isPrivate() { 19 | return ($this->flags & DECLARATION_TYPE_PRIVATE); 20 | } 21 | 22 | public function isStatic() { 23 | return ($this->flags & DECLARATION_TYPE_STATIC); 24 | } 25 | 26 | public function getName() { 27 | return $this->name; 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /tests/ArrayExample.php: -------------------------------------------------------------------------------- 1 | 'gordon'); 7 | $name = array($nameArray['name']); 8 | 9 | assert($name[0], 'gordon'); 10 | 11 | 12 | 13 | $dataMap = [ 14 | array('photoID', 'id'), 15 | ['owner', 'Danack'], 16 | ]; 17 | 18 | $gah = [1, 2]; 19 | 20 | 21 | $test = $gah[0]; 22 | 23 | $intArray2 = array( 24 | 1, 25 | 1 => 2, 26 | 2 => 3, 27 | 'subArray' => [ 28 | [1], 29 | 2, 30 | 3 => 3 31 | ], 32 | ); 33 | 34 | 35 | //JS function array_push_value(array, value){ 36 | //JS 37 | //JS for(var x=0 ; x<1000 ; x++){ 38 | //JS if(array.hasOwnProperty(x) == false){ 39 | //JS array[x] = value; 40 | //JS return; 41 | //JS } 42 | //JS } 43 | //JS 44 | //JS throw new Error("Can't push onto array - it is too large."); 45 | //JS } 46 | 47 | 48 | function sumArray($intArray){ 49 | 50 | $total = 0; 51 | 52 | foreach($intArray as $value){ 53 | $total += $value; 54 | } 55 | 56 | return $total; 57 | } 58 | 59 | //************************************************************* 60 | //************************************************************* 61 | 62 | //Shamoan 63 | 64 | $pushArray = array(); 65 | 66 | array_push($pushArray, 1); 67 | array_push($pushArray, 2); 68 | array_push($pushArray, 3); 69 | 70 | $value = sumArray($pushArray); 71 | 72 | assert($value, 6); 73 | 74 | //************************************************************* 75 | //************************************************************* 76 | 77 | $testVar = 3; 78 | 79 | $stringArray = array( 80 | 'Hello', 81 | ' ', 82 | 'world', 83 | ); 84 | 85 | $output = ''; 86 | 87 | foreach($stringArray as $string){ 88 | $output .= $string; 89 | } 90 | 91 | assert($output, "Hello world"); 92 | 93 | //************************************************************* 94 | //************************************************************* 95 | 96 | $intArray = array( 97 | 1, 98 | 1 => 2, 99 | 2 => 3, 100 | 'subArray' => array( 101 | 1, 102 | 2, 103 | 3 => 3 104 | ), 105 | ); 106 | 107 | 108 | 109 | //$value = sumArray($intArray); 110 | $value = sumArray($intArray['subArray']); 111 | 112 | assert($value, 6); 113 | 114 | //************************************************************* 115 | //************************************************************* 116 | 117 | class ArrayTestClass{ 118 | 119 | var $noteID = 123; 120 | 121 | function getTestArray(){ 122 | $params = array('noteID' => $this->noteID); 123 | return $params; 124 | } 125 | } 126 | 127 | $arrayTestClass = new ArrayTestClass(); 128 | 129 | $testArray = $arrayTestClass->getTestArray(); 130 | 131 | $value = sumArray($testArray); 132 | 133 | assert($value, 123); 134 | 135 | 136 | testEnd(); 137 | -------------------------------------------------------------------------------- /tests/AssigningThis.php: -------------------------------------------------------------------------------- 1 | five; 32 | } 33 | 34 | 35 | } 36 | 37 | $testClass = new TestClass(); 38 | 39 | assert($testClass->getThis(), $testClass); 40 | assert($testClass->getThis2(), $testClass); 41 | assert($testClass->getThis3(), $testClass); 42 | assert($testClass->getClassName(), 'TestClass'); 43 | assert($testClass->getValue(), 5); 44 | 45 | testEnd(); 46 | 47 | ?> -------------------------------------------------------------------------------- /tests/BugReports.php: -------------------------------------------------------------------------------- 1 | 'gordon'); 11 | 12 | 13 | 14 | 15 | $x = array($var3['name' ]); 16 | 17 | 18 | //Sent via email 19 | define('BOOL_VAL', true); 20 | define('INT_VAL', 5); 21 | define('FLOAT_VAL', 0.5); 22 | define('NULL_VAL', NULL); 23 | 24 | assert(BOOL_VAL, true); 25 | assert(INT_VAL * 2, 10); 26 | assert(FLOAT_VAL + 1, 1.5); 27 | assert(NULL_VAL, NULL); 28 | 29 | 30 | 31 | 32 | //Sent via email 33 | $boolTrue = true; 34 | $boolFalse = !$boolTrue; 35 | assert($boolFalse, false); 36 | 37 | 38 | 39 | class InstanceTest{} 40 | 41 | $instanceTest = new InstanceTest(); 42 | 43 | $correctInstance = false; 44 | 45 | if ($instanceTest instanceof InstanceTest){ 46 | $correctInstance = true; 47 | } 48 | 49 | assert($correctInstance, true); 50 | 51 | 52 | 53 | 54 | 55 | //https://github.com/Danack/PHP-to-Javascript/issues/33 56 | 57 | class ClassWithPrivate { 58 | private $foo = 4; 59 | 60 | function __construct(){ 61 | //$this->foo += 2; 62 | } 63 | 64 | public function accessPrivate(){ 65 | return $this->foo; 66 | } 67 | } 68 | 69 | $classWithPrivate = new ClassWithPrivate(); 70 | $classWithPrivate->accessPrivate(); 71 | 72 | 73 | assert($classWithPrivate->accessPrivate(), 4); 74 | 75 | 76 | 77 | //https://github.com/Danack/PHP-to-Javascript/issues/39 78 | 79 | class ClassWithPrivateProperty{ 80 | 81 | private $foo = 0; 82 | 83 | function __construct(){ 84 | $this->setFoo(5); 85 | } 86 | function setFoo($newFoo){ 87 | $this->foo = $newFoo; 88 | } 89 | 90 | function getFoo(){ 91 | return $this->foo; 92 | } 93 | } 94 | 95 | 96 | $classWithPrivateProperty = new ClassWithPrivateProperty(); 97 | 98 | assert($classWithPrivateProperty->getFoo(), 5); 99 | 100 | 101 | 102 | //https://github.com/Danack/PHP-to-Javascript/issues/35 103 | 104 | class Person { 105 | 106 | public $skillLevel = 0; 107 | 108 | function addSkill($skillLevel) { 109 | $this->$skillLevel += $skillLevel; 110 | } 111 | } 112 | 113 | 114 | 115 | $person1 = new Person(); 116 | $person1->addSkill(5); 117 | 118 | $person2 = new Person(); 119 | $person2->addSkill(6); 120 | 121 | assert($person1->skillLevel, 5); 122 | assert($person2->skillLevel, 6); 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | // https://github.com/Danack/PHP-to-Javascript/issues/31 134 | 135 | class Class1{ 136 | public static $instance; 137 | public $foo = "foo"; 138 | } 139 | 140 | class Class2{ 141 | function __construct(){ 142 | Class1::$instance->foo = 'foo'; 143 | } 144 | } 145 | 146 | 147 | //https://github.com/Danack/PHP-to-Javascript/issues/45 148 | 149 | 150 | //class ParentClass { 151 | // public $wth = "foo"; 152 | // 153 | // 154 | // public function foo() { 155 | // return "parent"; 156 | // } 157 | //} 158 | // 159 | //class ChildClass extends ParentClass { 160 | // 161 | // static public $wth2 = "hmm"; 162 | // 163 | // function __construct() { 164 | // echo parent::foo(); 165 | // echo self::$wth2; 166 | // } 167 | //} 168 | 169 | 170 | //$test = new ChildClass(); 171 | 172 | 173 | class TestClass { 174 | 175 | public $message; 176 | 177 | function __construct($message) { 178 | $this->message = $message; 179 | } 180 | 181 | function windowCloseFunction() { 182 | echo "Goodbye ".$this->message; 183 | } 184 | 185 | // //If you have jQuery 186 | // //JS window.onresize = $.proxy(this, 'windowCloseFunction'); 187 | 188 | //Or with standard Javascript 189 | // //JS this.makeWindowCloseFunction = function($context, $functionName) { 190 | // //JS return function() { 191 | // //JS $functionName.call($context); 192 | // //JS } 193 | // //JS} 194 | // //JS 195 | // //JS window.onresize = this.makeWindowCloseFunction(this, this.windowCloseFunction); 196 | } 197 | 198 | $test = new TestClass("cruel world!"); 199 | 200 | 201 | 202 | 203 | 204 | 205 | //https://github.com/Danack/PHP-to-Javascript/issues/44 206 | //Embedded variables 207 | 208 | $value1 = 5; 209 | 210 | $value2 = "Hello " . $value1 . " there"; 211 | assert($value2, "Hello 5 there"); 212 | 213 | $value3 = "Hello ${value1} there"; 214 | assert($value3, "Hello 5 there"); 215 | 216 | function greet($name) { 217 | return "Hello ${name}!"; 218 | } 219 | 220 | assert(greet("Bob"), "Hello Bob!"); 221 | 222 | 223 | //https://github.com/Danack/PHP-to-Javascript/issues/14 224 | //Modulus doesn't work 225 | 226 | $p2 = 8; 227 | $step = 6; 228 | 229 | $p2 -= ($p2 % $step); 230 | 231 | //assert($p2, 6); 232 | 233 | //************************************************************* 234 | //************************************************************* 235 | 236 | //https://github.com/Danack/PHP-to-Javascript/issues/15 237 | $countValue = 4; 238 | $countValue--; 239 | 240 | //assert($countValue, 3); 241 | 242 | $countValue--; 243 | 244 | //assert($countValue, 2); 245 | 246 | 247 | //https://github.com/Danack/PHP-to-Javascript/issues/48 248 | 249 | 250 | 251 | function add($value1, $value2 = -1) { 252 | return $value1 + $value2; 253 | } 254 | 255 | 256 | //assert(add(5, 5), 10); 257 | 258 | //assert(add(5), 4); 259 | 260 | 261 | 262 | testEnd(); 263 | 264 | 265 | 266 | ?> -------------------------------------------------------------------------------- /tests/ClassExample.php: -------------------------------------------------------------------------------- 1 | value = $initialValue; 13 | 14 | $this->testArray = array(); 15 | $this->testArray[0] = 1; 16 | $this->testArray[1] = 2; 17 | $this->testArray[2] = 3; 18 | } 19 | 20 | 21 | function testStatic(){ 22 | $currentValue = self::$staticVar; 23 | self::$staticVar++; 24 | return $currentValue; 25 | } 26 | 27 | function addValue($value){ 28 | $this->value += $value; 29 | } 30 | 31 | function getArrayValue(){ 32 | $result = 0; 33 | 34 | foreach($this->testArray as $testValue){ 35 | $result += $testValue; 36 | } 37 | 38 | return $result; 39 | } 40 | 41 | function getArrayValueWithIndex(){ 42 | $result = 0; 43 | 44 | foreach($this->testArray as $key => $testValue){ 45 | $result += $testValue; 46 | } 47 | 48 | return $result; 49 | } 50 | 51 | private function privateFunction(){ 52 | return 5; 53 | } 54 | 55 | public function publicAccess(){ 56 | return $this->privateFunction(); 57 | } 58 | 59 | public function testFunctionStatic(){ 60 | static $countUnique = 0; 61 | $countUnique++; 62 | 63 | return $countUnique; 64 | } 65 | 66 | 67 | } 68 | 69 | 70 | 71 | $classExample = new ClassExample(5); 72 | 73 | $classExample->addValue(5); 74 | 75 | assert($classExample->value, 10); 76 | 77 | assert($classExample->getArrayValue(), 6); 78 | 79 | assert($classExample->getArrayValueWithIndex(), 6); 80 | 81 | 82 | $classExample->testStatic(); 83 | $classExample->testStatic(); 84 | $result = $classExample->testStatic(); 85 | 86 | 87 | //Called two times, but value is only incremented twice 88 | assert($result, 2); 89 | 90 | 91 | 92 | 93 | $privateAccessed = false; 94 | $exceptionCaught = false; 95 | try{ 96 | //Yes IDE - I know this isn't allowed. 97 | ///** @noinspection PhpIllegalArrayKeyTypeInspection */ 98 | 99 | // @SuppressWarnings 100 | $classExample->privateFunction(); 101 | $privateAccessed = true; 102 | } 103 | catch(Exception $error){ 104 | //This correct - the private function should not be callable. 105 | $exceptionCaught = true; 106 | } 107 | 108 | assert($privateAccessed, false); 109 | assert($exceptionCaught, true); 110 | 111 | $value = $classExample->publicAccess(); 112 | assert($value, 5); 113 | 114 | 115 | $classExample->testFunctionStatic(); 116 | $classExample->testFunctionStatic(); 117 | $classExample->testFunctionStatic(); 118 | $value = $classExample->testFunctionStatic(); 119 | 120 | assert($value, 4); 121 | 122 | testEnd(); 123 | 124 | ?> -------------------------------------------------------------------------------- /tests/ClassSetVarExample.php: -------------------------------------------------------------------------------- 1 | '_content'], 12 | // ['description', 'description', 'unindex' => '_content', 'optional' => true], 13 | ]; 14 | 15 | public $instanceTestArray = array( 16 | 1, 2, 3 17 | ); 18 | 19 | static public $staticTestArray = array( 20 | 1, 2, 3, 4 21 | ); 22 | 23 | 24 | var $value = null; 25 | 26 | function __construct(){ 27 | 28 | echo "lol"; 29 | 30 | 31 | } 32 | 33 | function getInstanceArrayValue(){ 34 | $result = 0; 35 | 36 | foreach($this->instanceTestArray as $testValue){ 37 | $result += $testValue; 38 | } 39 | 40 | return $result; 41 | } 42 | 43 | function getStaticArrayValue(){ 44 | $result = 0; 45 | 46 | foreach(self::$staticTestArray as $testValue){ 47 | $result += $testValue; 48 | } 49 | 50 | return $result; 51 | } 52 | } 53 | 54 | 55 | 56 | $classExample = new ClassSetVarExample(); 57 | assert($classExample->getInstanceArrayValue(), 6); 58 | assert($classExample->getStaticArrayValue(), 10); 59 | testEnd(); 60 | 61 | ?> -------------------------------------------------------------------------------- /tests/Closure.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/DefaultValue.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/Inheritance.php: -------------------------------------------------------------------------------- 1 | adultClassVar = 123; 15 | return $this->adultClassVar; 16 | } 17 | } 18 | 19 | 20 | class Child extends Adult { 21 | 22 | static $childStaticVar; 23 | 24 | public $childInstanceVar; 25 | 26 | function __construct(){ 27 | $this->isExtended = true; 28 | } 29 | 30 | function childValue(){ 31 | 32 | // self::$childStaticVar = 123; 33 | 34 | $this->childInstanceVar = 123; 35 | 36 | // TODO - Adult static variable needs to be accessed via Adult.adultStaticVar not 37 | // Child.adultStaticVar in the generated javascript. Which may be tricky. 38 | 39 | // echo "This is a child method. \n" + $this->childInstanceVar + " " + self::$childStaticVar + " " + + $this->adultClassVar + " " + self::$adultStaticVar; 40 | 41 | return 12345; 42 | } 43 | } 44 | 45 | $adultOnly = new Adult(); 46 | 47 | assert($adultOnly->adultValue(), 123); 48 | assert($adultOnly->isExtended, false); 49 | 50 | $child = new Child(); 51 | 52 | assert($child->childValue(), 12345); 53 | assert($child->adultValue(), 123); 54 | assert($child->isExtended, true); 55 | 56 | 57 | testEnd(); 58 | 59 | ?> -------------------------------------------------------------------------------- /tests/MultiLine.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/PublicPrivate.php: -------------------------------------------------------------------------------- 1 | foo; 8 | } 9 | } 10 | 11 | $f = new SimplePrivate(); 12 | 13 | $value = $f->getFoo(); 14 | 15 | assert($value, 4); 16 | 17 | testEnd(); 18 | 19 | ?> -------------------------------------------------------------------------------- /tests/SimpleExample.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/SimpleOps.php: -------------------------------------------------------------------------------- 1 | register(); 13 | * 14 | * @author Jonathan H. Wage 15 | * @author Roman S. Borschel 16 | * @author Matthew Weier O'Phinney 17 | * @author Kris Wallsmith 18 | * @author Fabien Potencier 19 | */ 20 | class SplClassLoader 21 | { 22 | private $_fileExtension = '.php'; 23 | private $_namespace; 24 | private $_includePath; 25 | private $_namespaceSeparator = '\\'; 26 | 27 | /** 28 | * Creates a new SplClassLoader that loads classes of the 29 | * specified namespace. 30 | * 31 | * @param string $ns The namespace to use. 32 | */ 33 | public function __construct($ns = null, $includePath = null) 34 | { 35 | $this->_namespace = $ns; 36 | $this->_includePath = $includePath; 37 | } 38 | 39 | /** 40 | * Sets the namespace separator used by classes in the namespace of this class loader. 41 | * 42 | * @param string $sep The separator to use. 43 | */ 44 | public function setNamespaceSeparator($sep) 45 | { 46 | $this->_namespaceSeparator = $sep; 47 | } 48 | 49 | /** 50 | * Gets the namespace seperator used by classes in the namespace of this class loader. 51 | * 52 | * @return void 53 | */ 54 | public function getNamespaceSeparator() 55 | { 56 | return $this->_namespaceSeparator; 57 | } 58 | 59 | /** 60 | * Sets the base include path for all class files in the namespace of this class loader. 61 | * 62 | * @param string $includePath 63 | */ 64 | public function setIncludePath($includePath) 65 | { 66 | $this->_includePath = $includePath; 67 | } 68 | 69 | /** 70 | * Gets the base include path for all class files in the namespace of this class loader. 71 | * 72 | * @return string $includePath 73 | */ 74 | public function getIncludePath() 75 | { 76 | return $this->_includePath; 77 | } 78 | 79 | /** 80 | * Sets the file extension of class files in the namespace of this class loader. 81 | * 82 | * @param string $fileExtension 83 | */ 84 | public function setFileExtension($fileExtension) 85 | { 86 | $this->_fileExtension = $fileExtension; 87 | } 88 | 89 | /** 90 | * Gets the file extension of class files in the namespace of this class loader. 91 | * 92 | * @return string $fileExtension 93 | */ 94 | public function getFileExtension() 95 | { 96 | return $this->_fileExtension; 97 | } 98 | 99 | /** 100 | * Installs this class loader on the SPL autoload stack. 101 | */ 102 | public function register() 103 | { 104 | spl_autoload_register(array($this, 'loadClass')); 105 | } 106 | 107 | /** 108 | * Uninstalls this class loader from the SPL autoloader stack. 109 | */ 110 | public function unregister() 111 | { 112 | spl_autoload_unregister(array($this, 'loadClass')); 113 | } 114 | 115 | /** 116 | * Loads the given class or interface. 117 | * 118 | * @param string $className The name of the class to load. 119 | * @return void 120 | */ 121 | public function loadClass($className) 122 | { 123 | if (null === $this->_namespace || $this->_namespace.$this->_namespaceSeparator === substr($className, 0, strlen($this->_namespace.$this->_namespaceSeparator))) { 124 | $fileName = ''; 125 | $namespace = ''; 126 | if (false !== ($lastNsPos = strripos($className, $this->_namespaceSeparator))) { 127 | $namespace = substr($className, 0, $lastNsPos); 128 | $className = substr($className, $lastNsPos + 1); 129 | $fileName = str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; 130 | } 131 | $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . $this->_fileExtension; 132 | 133 | $fullFileName = ($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : '') . $fileName; 134 | 135 | if(file_exists($fullFileName) == false){ 136 | xdebug_break(); 137 | } 138 | 139 | require ($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : '') . $fileName; 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /tests/StaticAccessor.php: -------------------------------------------------------------------------------- 1 | foo = 'Class2'; 17 | } 18 | } 19 | 20 | 21 | $test = new Class1(); 22 | $test2 = new Class2(); 23 | 24 | 25 | 26 | assert($test->foo, 'Class2'); 27 | 28 | 29 | 30 | 31 | ?> -------------------------------------------------------------------------------- /tests/StaticTest.php: -------------------------------------------------------------------------------- 1 | staticClassVar; 12 | } 13 | 14 | function instanceMethod(){ 15 | $this->instanceVar++; 16 | //echo $this->instanceVar; 17 | } 18 | 19 | function methodWithStatic(){ 20 | static $staticVar = 0; 21 | $staticVar++; 22 | //echo $staticVar; 23 | return $staticVar; 24 | } 25 | } 26 | 27 | 28 | StaticTest::staticMethod(); 29 | 30 | 31 | $staticTest = new StaticTest(); 32 | 33 | $staticTest->instanceMethod(); 34 | 35 | $staticTest->methodWithStatic(); 36 | $staticTest->methodWithStatic(); 37 | $staticTest->methodWithStatic(); 38 | 39 | assert(StaticTest::$staticClassVar, 1); 40 | assert($staticTest->instanceVar, 1); 41 | assert($staticTest->methodWithStatic(), 4); 42 | 43 | testEnd(); 44 | 45 | ?> -------------------------------------------------------------------------------- /tests/SwitchStatement.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/Template.php: -------------------------------------------------------------------------------- 1 | 5 | 6 | This is outside of PHP. 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/Ternary.php: -------------------------------------------------------------------------------- 1 | objectID = $objectID; 19 | $this->name = $name; 20 | $this->value = $value; 21 | } 22 | 23 | function test(){ 24 | return "name = ".$this->name." value = ".$this->value; 25 | } 26 | } 27 | 28 | $testObject = new ExampleJSON(1, "First", "Testing"); 29 | 30 | $json = $testObject->toJSON(); 31 | 32 | $duplicate = ExampleJSON::factory($json); 33 | 34 | assert($duplicate->name == "First", TRUE); 35 | assert($duplicate->value == "Testing", TRUE); 36 | 37 | testEnd(); 38 | 39 | ?> -------------------------------------------------------------------------------- /tests/TraitInclude.php: -------------------------------------------------------------------------------- 1 | $value){ 12 | $object->$key = $value; 13 | } 14 | 15 | return $object; 16 | } 17 | 18 | function toJSON(){ 19 | 20 | $className = get_class($this); 21 | 22 | return json_encode_object($this, $className); 23 | } 24 | } 25 | 26 | testEnd(); 27 | 28 | ?> -------------------------------------------------------------------------------- /tests/TryCatch.php: -------------------------------------------------------------------------------- 1 | getMessage(); 19 | $exceptionMessage = $e->getMessage(); 20 | } 21 | 22 | assert($exceptionCaught, true); 23 | assertGreater(strlen($exceptionMessage), 5); 24 | 25 | 26 | $thrownExceptionCaught = false; 27 | try{ 28 | throw new Exception("What is this?"); 29 | } 30 | catch(Exception $e){ 31 | //echo "Exception caught ".$e->getMessage(); 32 | $thrownExceptionCaught = true; 33 | } 34 | 35 | assert($thrownExceptionCaught, true); 36 | 37 | testEnd(); 38 | 39 | ?> -------------------------------------------------------------------------------- /tests/TypeHinting.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/UnsetTest.php: -------------------------------------------------------------------------------- 1 | 1, 27 | 'index2' => 2, 28 | 'index3' => 3, 29 | ); 30 | 31 | 32 | 33 | $unsetArray = testUnset($array); 34 | 35 | 36 | $result = sumArray($unsetArray); 37 | 38 | 39 | assert($result, 4); 40 | 41 | testEnd(); 42 | 43 | ?> -------------------------------------------------------------------------------- /tests/continue.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/examples.php: -------------------------------------------------------------------------------- 1 | register(); 7 | 8 | $exportPath = "export"; 9 | 10 | error_reporting(E_ALL); 11 | 12 | $filesToConvert = array( 13 | 'ArrayExample.js' => 'ArrayExample.php', 14 | 'AssigningThis.js' => 'AssigningThis.php', 15 | 'BugReports.js' => 'BugReports.php', 16 | 'ClassExample.js' => 'ClassExample.php', 17 | 'Closure.js' => 'Closure.php', 18 | 'continue.js' => 'continue.php', 19 | 'countItems.js' => 'countItems.php', 20 | 'CustomEvent.js' => 'CustomEvent.php' , 21 | 'DefaultValue.js' => 'DefaultValue.php', 22 | 'Inheritance.js' => 'Inheritance.php', 23 | 24 | 'NameSpace.js' => 'NameSpace.php', 25 | //Broken test 26 | 'PublicPrivate.js' => 'PublicPrivate.php', 27 | 'SimpleExample.js' => 'SimpleExample.php', 28 | 29 | 'StaticTest.js' => 'StaticTest.php', 30 | 31 | 'SwitchStatement.js' => 'SwitchStatement.php', 32 | 'TryCatch.js' => 'TryCatch.php', 33 | 'TraitExample.js' => array( 34 | 'TraitInclude.php', 35 | 'TraitExample.php', 36 | ), 37 | 'TypeHinting.js' => 'TypeHinting.php', 38 | ); 39 | 40 | //$filesToConvert = array( 41 | // 'ArrayExample.js' => 'ArrayExample.php', 42 | //); 43 | 44 | 45 | $convertedFiles = array(); 46 | 47 | function generateTestPage($convertedFiles){ 48 | 49 | $fileHandle = fopen("output/test.html", "w"); 50 | 51 | fwrite($fileHandle, "Tests are loaded via javascript into this webpage.
 
If nothing turns green then probably the conversion failed completely, and either the Javascript files are not present or so invalid that the can't be compiled.
 
"); 52 | 53 | foreach($convertedFiles as $convertedFile){ 54 | $testID = str_replace(".", "_", $convertedFile); 55 | $testIDStatus = $testID."_status"; 56 | fwrite($fileHandle, "
$convertedFile - 57 | Not tested. 58 | 59 | Incomplete. 60 | 61 |
"); 62 | } 63 | 64 | fwrite($fileHandle, ""); 65 | 66 | fwrite($fileHandle, ""); 67 | fwrite($fileHandle, ""); 68 | fwrite($fileHandle, ""); 69 | 70 | foreach($convertedFiles as $convertedFile){ 71 | $testID = str_replace(".", "_", $convertedFile); 72 | fwrite($fileHandle, "\n"); 76 | 77 | fwrite($fileHandle, "\n"); 78 | $testID = str_replace(".", "_", $convertedFile); 79 | fwrite($fileHandle, "\n"); 82 | } 83 | 84 | fwrite($fileHandle, ""); 85 | fclose($fileHandle); 86 | } 87 | 88 | function encapsulateJavascriptFile($outputFilename) { 89 | $fileContents = file_get_contents($outputFilename); 90 | $fileContents = "(function(){\n".$fileContents."})();\n"; 91 | file_put_contents($outputFilename, $fileContents); 92 | } 93 | 94 | foreach($filesToConvert as $outputFilename => $inputFileList ){ 95 | $phpToJavascript = new PHPToJavascript\PHPToJavascript(); 96 | 97 | // $phpToJavascript->setTrace(true); 98 | 99 | $phpToJavascript->setEchoConversionFunction(PHPToJavascript\PHPToJavascript::$ECHO_TO_ALERT); 100 | 101 | $inputFilename = ""; 102 | 103 | if(is_array($inputFileList) == TRUE){ 104 | foreach($inputFileList as $inputFile){ 105 | $phpToJavascript->addFromFile($inputFile); 106 | $inputFilename = $inputFile." "; 107 | } 108 | } 109 | else{ 110 | $phpToJavascript->addFromFile($inputFileList); 111 | $inputFilename = $inputFileList; 112 | } 113 | 114 | $phpToJavascript->addPostConversionReplace("//JS", ""); 115 | 116 | $phpToJavascript->generateFile("output/".$outputFilename, $inputFilename); 117 | 118 | encapsulateJavascriptFile("output/".$outputFilename); 119 | 120 | $convertedFiles[] = $outputFilename; 121 | } 122 | 123 | 124 | generateTestPage($convertedFiles); 125 | 126 | 127 | function testEnd(){ 128 | 129 | } 130 | 131 | ?> -------------------------------------------------------------------------------- /tests/output/doNotDelete.txt: -------------------------------------------------------------------------------- 1 | This just makes sure the directory is created. -------------------------------------------------------------------------------- /tests/testStart.js: -------------------------------------------------------------------------------- 1 | 2 | var testsRunForModule = 0; 3 | var testsRunTotal = 0; 4 | var testsPassedForModule = 0; 5 | var testsPassedTotal = 0; 6 | 7 | var testErrors = []; 8 | 9 | function assert(var1, var2){ 10 | testsRunForModule++; 11 | testsRunTotal++; 12 | 13 | if(var1 != var2){ 14 | testErrors.push("Test " + testsRunForModule + "assert failed " + var1 + " != " + var2 ); 15 | debugger; 16 | } 17 | else{ 18 | testsPassedForModule++; 19 | testsPassedTotal++ 20 | } 21 | } 22 | 23 | function assertGreater(var1, var2){ 24 | testsRunForModule++; 25 | testsRunTotal++; 26 | 27 | if(!(var1 > var2)){ 28 | testErrors.push("Test " + testsRunForModule + "assert failed !(" + var1 + " > " + var2 + ")"); 29 | } 30 | else{ 31 | testsPassedForModule++; 32 | testsPassedTotal++ 33 | } 34 | } 35 | 36 | 37 | function setTestsResult(domID){ 38 | 39 | var string = "No tests run"; 40 | 41 | if(testsRunForModule > 0){ 42 | if(testsPassedForModule == testsRunForModule){ 43 | string = "All tests passed " + testsPassedForModule + " / " + testsRunForModule+ " "; 44 | } 45 | else if(testsPassedForModule > 0){ 46 | string = "Some tests passed " + testsPassedForModule + " / " + testsRunForModule + " "; 47 | } 48 | else{ 49 | string = "All tests failed"; 50 | } 51 | } 52 | 53 | 54 | for(var i in testErrors){ 55 | var testError = testErrors[i]; 56 | string = string + "
" + testError; 57 | } 58 | 59 | document.getElementById(domID).innerHTML = string; 60 | 61 | testsRunForModule = 0; 62 | testsPassedForModule = 0; 63 | testErrors = []; 64 | } 65 | 66 | 67 | function json_encode_object(objectToEncode, objectType){ 68 | 69 | if(objectType === undefined){ 70 | objectType = "UnknownObjectType"; 71 | } 72 | 73 | var params = {}; 74 | 75 | for(var name in objectToEncode){ 76 | if(objectToEncode.hasOwnProperty(name)){ 77 | 78 | var propertyValue = objectToEncode[name]; 79 | 80 | if (propertyValue instanceof Function) { 81 | //skip it. 82 | } 83 | else{ 84 | params[name] = propertyValue; 85 | } 86 | } 87 | } 88 | 89 | params.ObjectType = objectType; 90 | 91 | return JSON.stringify(params); 92 | } 93 | 94 | function json_decode(jsonString){ 95 | return jQuery.parseJSON(jsonString); 96 | } 97 | 98 | 99 | // {{{ get_class 100 | function get_class(obj) { 101 | // Returns the name of the class of an object 102 | // 103 | // + discuss at: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_get_class/ 104 | // + version: 809.522 105 | // + original by: Ates Goral (http://magnetiq.com) 106 | // + improved by: David James 107 | // * example 1: get_class(new (function MyClass() {})); 108 | // * returns 1: "MyClass" 109 | // * example 2: get_class({}); 110 | // * returns 2: "Object" 111 | // * example 3: get_class([]); 112 | // * returns 3: false 113 | // * example 4: get_class(42); 114 | // * returns 4: false 115 | // * example 5: get_class(window); 116 | // * returns 5: false 117 | // * example 6: get_class(function MyFunction() {}); 118 | // * returns 6: false 119 | 120 | if (obj instanceof Object && !(obj instanceof Array) 121 | && !(obj instanceof Function) && obj.constructor 122 | && obj != window) { 123 | var arr = obj.constructor.toString().match(/function\s*(\w+)/); 124 | 125 | if (arr && arr.length == 2) { 126 | return arr[1]; 127 | } 128 | } 129 | 130 | return false; 131 | }// }}} 132 | 133 | var runningTestID = null; 134 | 135 | function testStart(testName){ 136 | runningTestID = testName; 137 | } 138 | 139 | function testEnd(){ 140 | if(runningTestID != null){ 141 | document.getElementById(runningTestID).innerHTML = "Completed."; 142 | runningTestID = null; 143 | } 144 | } 145 | --------------------------------------------------------------------------------