describe('PHP Engine __DIR__ magic constant expression integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'capturing current file\'s directory from initial program code': { code: nowdoc(function () {/*<<<EOS <?php echo __DIR__; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: '(program)' }, 'capturing current file\'s directory in required module': { code: nowdoc(function () {/*<<<EOS <?php require_once 'vendor/producer/get_dir.php'; EOS */;}), // jshint ignore:line options: { include: function (path, promise) { promise.resolve(nowdoc(function () {/*<<<EOS <?php echo __DIR__; EOS */;})); // jshint ignore:line } }, expectedResult: null, expectedStderr: '', expectedStdout: 'vendor/producer' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine subtraction assignment "-=" operator integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'subtracting 4 from a variable containing integer': { code: '<?php $num = 7; $num -= 4; return $num;', expectedResult: 3, expectedResultType: 'integer', expectedStderr: '', expectedStdout: '' }, 'subtracting 4 from a variable containing float': { code: '<?php $num = 6.0; $num -= 4; return $num;', expectedResult: 2, expectedResultType: 'float', expectedStderr: '', expectedStdout: '' }, 'attempting to subtract 5 from undefined variable': { code: '<?php $num -= 5; var_dump($num); print "Done";', expectedResult: null, expectedStderr: nowdoc(function () {/*<<<EOS PHP Notice: Undefined variable: num EOS */;}), // jshint ignore:line // Note that the 'Done' echo following the dump must be executed, this is only a notice expectedStdout: nowdoc(function () {/*<<<EOS int(-5) Done EOS */;}) // jshint ignore:line } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('inside foreach (...) {...}', function () { _.each({ 'foreach should reset internal pointer but not advance during': { code: nowdoc(function () {/*<<<EOS <?php $array = array(1, 2, 3); $result = ''; // Advance to next element before loop, to check internal pointer is reset by foreach next($array); foreach ($array as $val) { $result = $result . current($array) . ','; } return $result; EOS */;}), // jshint ignore:line expectedResult: '1,1,1,', expectedStderr: '', expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
include: function (path, promise) { promise.resolve(nowdoc(function () {/*<<<EOS <?php echo __DIR__; EOS */;})); // jshint ignore:line }
describe('PHP Engine class statement instance method type hinting integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'passing array to method argument type hinted as array': { code: nowdoc(function () {/*<<<EOS <?php class Math { public function sum(array $numbers) { $total = 0; foreach ($numbers as $number) { $total = $total + $number; } return $total; } } $math = new Math; return $math->sum(array(3, 5, 1)); EOS */;}), // jshint ignore:line expectedResult: 9, expectedResultType: 'integer', expectedStderr: '', expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine closure expression default argument value integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'self-executed closure with default argument value': { code: nowdoc(function () {/*<<<EOS <?php $result = 4; function ($number = 10) use (&$result) { $result = $number; }(); return $result; EOS */;}), // jshint ignore:line expectedResult: 10, expectedStderr: '', expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine __FILE__ magic constant expression integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'capturing current file from initial program code': { code: nowdoc(function () {/*<<<EOS <?php echo __FILE__; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: '(program)' }, 'capturing current file in required module': { code: nowdoc(function () {/*<<<EOS <?php require_once 'get_file.php'; EOS */;}), // jshint ignore:line options: { include: function (path, promise) { promise.resolve(nowdoc(function () {/*<<<EOS <?php echo __FILE__; EOS */;})); // jshint ignore:line } }, expectedResult: null, expectedStderr: '', expectedStdout: 'get_file.php' }, // Ensure the state is not shared between main program and required module 'capturing current file in main program before and after required module': { code: nowdoc(function () {/*<<<EOS <?php echo __FILE__; require_once 'get_file.php'; echo __FILE__; EOS */;}), // jshint ignore:line options: { include: function (path, promise) { promise.resolve(nowdoc(function () {/*<<<EOS <?php echo __FILE__; EOS */;})); // jshint ignore:line } }, expectedResult: null, expectedStderr: '', expectedStdout: '(program)get_file.php(program)' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
var ace = global.ace, nowdoc = require('nowdoc'), javascriptCode = '/*global phpCode, resultBody, uniter */\n' + nowdoc(function () {/*<<<EOS 'use strict'; var phpEngine = uniter.createEngine('PHP'); phpEngine.expose({ getCC: function () { return 'en'; }, salutation: 'Hello' }, 'info'); phpEngine.getStdout().on('data', function (data) { print(data); }); phpEngine.getStderr().on('data', function (data) { print(data); }); phpEngine.execute(phpCode).fail(function (error) { print(error.toString()); }); EOS */ }), javascriptEditor, phpCode = nowdoc(function () {/*<<<EOS
describe('PHP Engine class statement public static property integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'defining static property without initial value and reading from outside class': { code: nowdoc(function () {/*<<<EOS <?php class Animal { public static $planet; } return Animal::$planet; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: '' }, 'defining static property with initial value and reading from outside class': { code: nowdoc(function () {/*<<<EOS <?php class Animal { public static $planet = 'Earth'; } return Animal::$planet; EOS */;}), // jshint ignore:line expectedResult: 'Earth', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'trying to read public static property as instance property from outside class': { code: nowdoc(function () {/*<<<EOS <?php class Animal { public static $species = 'Human'; } return (new Animal)->species; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: nowdoc(function () {/*<<<EOS PHP Strict standards: Accessing static property Animal::$species as non static PHP Notice: Undefined property: Animal::$species EOS */;}), // jshint ignore:line expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('exposing as global PHP variables', function () { _.each({ 'plain object from JavaScript with instance method': { code: nowdoc(function () {/*<<<EOS <?php return $tools->addThisDotMyvalueTo(16); EOS */;}), // jshint ignore:line expose: { 'tools': { addThisDotMyvalueTo: function (number) { return number + this.myValue; }, myValue: 5 } }, expectedResult: 21, expectedResultType: 'integer', expectedStderr: '', expectedStdout: '' }, 'plain object from JavaScript with prototype method': { code: nowdoc(function () {/*<<<EOS <?php return $tools->getValue(); EOS */;}), // jshint ignore:line expose: { 'tools': Object.create({ getValue: function () { return 'me'; } }) }, expectedResult: 'me', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'calling PHP closure from JS': { code: nowdoc(function () {/*<<<EOS <?php $tools->setAdder(function ($num1, $num2) { return $this->start + $num1 + $num2; }); return $tools->getValue(4, 2); EOS */;}), // jshint ignore:line expose: function () { var adder; return { 'tools': { getValue: function (num1, num2) { return adder.call({start: 10}, num1, num2); }, setAdder: function (newAdder) { adder = newAdder; } } }; }, expectedResult: 16, expectedResultType: 'integer', expectedStderr: '', expectedStdout: '' }, 'calling PHP closure from JS with null thisObj': { code: nowdoc(function () {/*<<<EOS <?php $tools->setCallback(function () { var_dump($this); }); return $tools->callBack(4, 2); EOS */;}), // jshint ignore:line expose: function () { var callback; return { 'tools': { callBack: function () { callback.call(null); }, setCallback: function (newCallback) { callback = newCallback; } } }; }, expectedResult: null, expectedResultType: 'null', expectedStderr: '', expectedStdout: nowdoc(function () {/*<<<EOS NULL EOS */;}) // jshint ignore:line } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine goto statement integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'jumping over first echo to second': { code: nowdoc(function () {/*<<<EOS <?php goto second; echo 'first'; second: echo 'second'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'second' }, 'jumping over unused label': { code: nowdoc(function () {/*<<<EOS <?php goto second; first: echo 'first'; second: echo 'second'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'second' }, 'jumping over first echo to second inside if with falsy condition': { code: nowdoc(function () {/*<<<EOS <?php goto second; echo 'first'; if (0) { second: echo 'second'; } EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'second' }, 'jumping over first echo to third inside if with falsy condition': { code: nowdoc(function () {/*<<<EOS <?php goto second; echo 'first'; if (0) { echo 'second'; second: echo 'third'; } EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'third' }, 'jumping over first if statement to echo inside second if with falsy condition': { code: nowdoc(function () {/*<<<EOS <?php goto end; echo 'first'; if (true) { echo 'second'; } if (0) { echo 'third'; end: echo 'fourth'; } EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'fourth' }, 'jumping out of if statement': { code: nowdoc(function () {/*<<<EOS <?php echo 'first'; if (true) { echo 'second'; goto end; echo 'third'; } echo 'fourth'; end: echo 'fifth'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'firstsecondfifth' }, 'jumping out of first if statement to echo inside second if': { code: nowdoc(function () {/*<<<EOS <?php echo 'first'; if (true) { echo 'second'; goto sixth; echo 'third'; } echo 'fourth'; if (0) { echo 'fifth'; sixth: echo 'sixth'; } echo 'seventh'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'firstsecondsixthseventh' }, 'jumping into nested if': { code: nowdoc(function () {/*<<<EOS <?php echo 'first'; goto end; echo 'second'; if (0) { echo 'third'; if (0) { echo 'fourth'; end: echo 'fifth'; } echo 'sixth'; } echo 'seventh'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'firstfifthsixthseventh' }, 'jumping out of nested if': { code: nowdoc(function () {/*<<<EOS <?php echo 'first'; if (true) { echo 'second'; if (true) { echo 'third'; goto end; echo 'fourth'; } echo 'fifth'; } echo 'sixth'; end: echo 'seventh'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'firstsecondthirdseventh' }, 'jumping out of nested if into nested if': { code: nowdoc(function () {/*<<<EOS <?php echo 'first'; if (true) { echo 'second'; if (true) { echo 'third'; goto end; echo 'fourth'; } echo 'fifth'; } echo 'sixth'; if (false) { echo 'seventh'; if (false) { echo 'eighth'; end: echo 'ninth'; } echo 'tenth'; } echo 'eleventh'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'firstsecondthirdninthtentheleventh' }, 'jumping backward into if': { code: nowdoc(function () {/*<<<EOS <?php echo 'first'; if (0) { echo 'second'; backward: echo 'third'; return; echo 'fourth'; } echo 'fifth'; goto backward; echo 'sixth'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'firstfifththird' }, 'jumping backward from if into previous if': { code: nowdoc(function () {/*<<<EOS <?php echo 'first'; if (0) { echo 'second'; backward: echo 'third'; return; echo 'fourth'; } echo 'fifth'; if (true) { echo 'sixth'; goto backward; echo 'seventh'; } echo 'eighth'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'firstfifthsixththird' }, 'reusing label name inside function': { code: nowdoc(function () {/*<<<EOS <?php echo 'first'; function doIt() { echo 'second'; goto myLabel; echo 'third'; myLabel: echo 'fourth'; } echo 'fifth'; doIt(); echo 'sixth'; goto myLabel; echo 'seventh'; myLabel: echo 'eighth'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'firstfifthsecondfourthsixtheighth' }, 'invalid jump into while loop': { code: nowdoc(function () {/*<<<EOS <?php goto invalid; while (0) { invalid: } EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: 'goto' into loop or switch statement is disallowed$/ }, expectedStderr: 'PHP Fatal error: \'goto\' into loop or switch statement is disallowed', expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine scope resolution operator "::" constant integratio', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'reading defined class constant from statically specified class name': { code: nowdoc(function () {/*<<<EOS <?php class Stuff { const CATEGORY = 'Misc'; } return Stuff::CATEGORY; EOS */;}), // jshint ignore:line expectedResult: 'Misc', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'reading defined class constant from instance variable': { code: nowdoc(function () {/*<<<EOS <?php class Stuff { const TEST = 'cmp'; } $object = new Stuff; return $object::TEST; EOS */;}), // jshint ignore:line expectedResult: 'cmp', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'attempting to read undefined class constant from statically specified class name': { code: nowdoc(function () {/*<<<EOS <?php class Stuff {} return Stuff::THINGS; EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Undefined class constant 'THINGS'$/ }, expectedStderr: 'PHP Fatal error: Undefined class constant \'THINGS\'', expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine constant expression integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'assigning undefined constant called "MY_CONST" to variable in global namespace': { code: nowdoc(function () {/*<<<EOS <?php $value = MY_CONST; return $value; EOS */;}), // jshint ignore:line // Undefined constant should be interpreted as bareword string literal expectedResult: 'MY_CONST', expectedResultType: 'string', expectedStderr: 'PHP Notice: Use of undefined constant MY_CONST - assumed \'MY_CONST\'\n', expectedStdout: '' }, 'assigning undefined constant called "YOUR_CONST" to variable in global namespace': { code: nowdoc(function () {/*<<<EOS <?php $value = YOUR_CONST; return $value; EOS */;}), // jshint ignore:line // Undefined constant should be interpreted as bareword string literal expectedResult: 'YOUR_CONST', expectedResultType: 'string', expectedStderr: 'PHP Notice: Use of undefined constant YOUR_CONST - assumed \'YOUR_CONST\'\n', expectedStdout: '' }, 'assigning undefined constant called "MY_CONST" to variable in a namespace': { code: nowdoc(function () {/*<<<EOS <?php namespace Us; $value = MY_CONST; return $value; EOS */;}), // jshint ignore:line // Undefined constant should be interpreted as bareword string literal expectedResult: 'MY_CONST', expectedResultType: 'string', expectedStderr: 'PHP Notice: Use of undefined constant MY_CONST - assumed \'MY_CONST\'\n', expectedStdout: '' }, 'undefined constant as default argument value when not called': { code: nowdoc(function () {/*<<<EOS <?php function test($value = UNDEF_CONST) {} EOS */;}), // jshint ignore:line expectedResult: null, // No notice should be raised expectedStderr: '', expectedStdout: '' }, 'undefined constant as default argument value when called and used': { code: nowdoc(function () {/*<<<EOS <?php function test($value = UNDEF_CONST) { return $value; } return test(); EOS */;}), // jshint ignore:line // Undefined constant should be interpreted as bareword string literal expectedResult: 'UNDEF_CONST', expectedResultType: 'string', expectedStderr: 'PHP Notice: Use of undefined constant UNDEF_CONST - assumed \'UNDEF_CONST\'\n', expectedStdout: '' }, // Ensure we use .hasOwnProperty(...) checks internally 'undefined constant called "constructor" as default argument value when called and used': { code: nowdoc(function () {/*<<<EOS <?php function test($value = constructor) { return $value; } return test(); EOS */;}), // jshint ignore:line // Undefined constant should be interpreted as bareword string literal expectedResult: 'constructor', expectedResultType: 'string', expectedStderr: 'PHP Notice: Use of undefined constant constructor - assumed \'constructor\'\n', expectedStdout: '' }, 'undefined constant as default argument value when called but not used': { code: nowdoc(function () {/*<<<EOS <?php function test($value = UNDEF_CONST) { return $value; } return test('world'); EOS */;}), // jshint ignore:line expectedResult: 'world', expectedResultType: 'string', // No notice should be raised expectedStderr: '', expectedStdout: '' }, 'defined constant as default argument value should be read and not raise warning when called and used': { code: nowdoc(function () {/*<<<EOS <?php define('MY_PLANET', 'Earth'); function getPlanet($name = MY_PLANET) { return $name; } return getPlanet(); EOS */;}), // jshint ignore:line expectedResult: 'Earth', expectedResultType: 'string', // No notice should be raised expectedStderr: '', expectedStdout: '' }, 'defined constant in namespace read from another namespace': { code: nowdoc(function () {/*<<<EOS <?php namespace Test; define('Test\NAME', 'Dan'); namespace Fun; return \Test\NAME; EOS */;}), // jshint ignore:line expectedResult: 'Dan', expectedResultType: 'string', // No notice should be raised expectedStderr: '', expectedStdout: '' }, 'defined constant in namespace read from another namespace via import': { code: nowdoc(function () {/*<<<EOS <?php namespace Test; use My as Me; define('My\NAME', 'Dan'); return Me\NAME; EOS */;}), // jshint ignore:line expectedResult: 'Dan', expectedResultType: 'string', // No notice should be raised expectedStderr: '', expectedStdout: '' }, 'constant from global namespace should be used if not defined in current one, case-insensitive': { code: nowdoc(function () {/*<<<EOS <?php namespace Test; define('PLANET', 'Earth', true); return pLaNET; EOS */;}), // jshint ignore:line expectedResult: 'Earth', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'constant from global namespace should not be used if not defined in current one, case-sensitive': { code: nowdoc(function () {/*<<<EOS <?php namespace Test; define('PLANET', 'Earth', false); return pLaNET; EOS */;}), // jshint ignore:line expectedResult: 'pLaNET', expectedResultType: 'string', expectedStderr: 'PHP Notice: Use of undefined constant pLaNET - assumed \'pLaNET\'\n', expectedStdout: '' }, 'attempting to read undefined constant from global namespace with prefix': { code: nowdoc(function () {/*<<<EOS <?php return \NAME; EOS */;}), // jshint ignore:line // Note that when using namespaces, use of undefined constant is a fatal error not just a notice expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Undefined constant 'NAME'$/ }, expectedStderr: 'PHP Fatal error: Undefined constant \'NAME\'', expectedStdout: '' }, 'attempting to read undefined constant from another namespace': { code: nowdoc(function () {/*<<<EOS <?php namespace Fun; return My\Stuff\NAME; EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Undefined constant 'Fun\\My\\Stuff\\NAME'$/ }, expectedStderr: 'PHP Fatal error: Undefined constant \'Fun\\My\\Stuff\\NAME\'', expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine function definition statement integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'simple function call': { code: '<?php function show($string) { echo $string; } show("hello!");', expectedResult: null, expectedStderr: '', expectedStdout: 'hello!' }, 'make sure variables defined in inner scopes are not defined in the outer one': { code: '<?php function doSomething() { $a = 1; } echo $a;', expectedResult: null, expectedStderr: 'PHP Notice: Undefined variable: a\n', expectedStdout: '' }, 'make sure variables defined in outer scopes are not defined in the inner one': { code: '<?php $a = 1; function doSomething() { echo $a; } doSomething();', expectedResult: null, expectedStderr: 'PHP Notice: Undefined variable: a\n', expectedStdout: '' }, // Test for pre-hoisting 'calling a function before its definition outside of any blocks eg. conditionals': { code: nowdoc(function () {/*<<<EOS <?php return add1(7); function add1($number) { return $number + 1; } EOS */;}), // jshint ignore:line expectedResult: 8, expectedStderr: '', expectedStdout: '' }, 'calling a function before its definition where definition is inside of a conditional': { code: nowdoc(function () {/*<<<EOS <?php return add1(7); if (true) { function add1($number) { return $number + 1; } } EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to undefined function add1\(\)$/ }, expectedStderr: 'PHP Fatal error: Call to undefined function add1()', expectedStdout: '' }, 'calling a function before its definition where definition is inside of a function': { code: nowdoc(function () {/*<<<EOS <?php function declareFunc() { secondFunc(); function secondFunc() {} } declareFunc(); EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to undefined function secondFunc\(\)$/ }, expectedStderr: 'PHP Fatal error: Call to undefined function secondFunc()', expectedStdout: '' }, 'calling a function before its definition where definition is inside of a while loop': { code: nowdoc(function () {/*<<<EOS <?php $a = 1; while ($a--) { doSomething(); function doSomething () {} } EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to undefined function doSomething\(\)$/ }, expectedStderr: 'PHP Fatal error: Call to undefined function doSomething()', expectedStdout: '' }, 'calling a function before its definition where definition is inside of a foreach loop': { code: nowdoc(function () {/*<<<EOS <?php $items = array(1); foreach ($items as $item) { doSomething(); function doSomething () {} } EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to undefined function doSomething\(\)$/ }, expectedStderr: 'PHP Fatal error: Call to undefined function doSomething()', expectedStdout: '' }, 'using the name "tools" for a function argument': { code: nowdoc(function () {/*<<<EOS <?php function getResult($tools) { return $tools->result; } $tools = new stdClass; $tools->result = 7; return getResult($tools); EOS */;}), // jshint ignore:line expectedResult: 7, expectedStderr: '', expectedStdout: '' }, 'function declarations inside conditionals should not be hoisted within the block': { code: nowdoc(function () {/*<<<EOS <?php if (true) { doSomething(); function doSomething() {} } EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to undefined function doSomething\(\)$/ }, expectedStderr: 'PHP Fatal error: Call to undefined function doSomething()', expectedStdout: '' }, 'attempting to call undefined function in the global namespace with same name as in current': { code: nowdoc(function () {/*<<<EOS <?php namespace My\Stuff; function my_func() {} \my_func(); EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to undefined function my_func\(\)$/ }, expectedStderr: 'PHP Fatal error: Call to undefined function my_func()', expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('when given one argument', function () { _.each({ 'empty array': { value: 'array()', expectedStdout: nowdoc(function () {/*<<<EOS array(0) { } EOS */;}) // jshint ignore:line }, 'array with one element with implicit key': { value: 'array(7)', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [0]=> int(7) } EOS */;}) // jshint ignore:line }, 'array with one element with explicit key': { value: 'array(4 => "a")', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [4]=> string(1) "a" } EOS */;}) // jshint ignore:line }, 'array with one element with explicit key for subarray value': { value: 'array(5 => array(6, 7))', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [5]=> array(2) { [0]=> int(6) [1]=> int(7) } } EOS */;}) // jshint ignore:line }, 'boolean true': { value: 'true', expectedStdout: 'bool(true)\n' }, 'boolean false': { value: 'false', expectedStdout: 'bool(false)\n' }, 'float': { value: '2.2', expectedStdout: 'float(2.2)\n' }, 'integer': { value: '3', expectedStdout: 'int(3)\n' }, 'null': { value: 'null', expectedStdout: 'NULL\n' }, 'empty stdClass instance': { value: 'new stdClass', expectedStdout: nowdoc(function () {/*<<<EOS object(stdClass)#1 (0) { } EOS */;}) // jshint ignore:line }, 'stdClass instance with one property': { value: 'new stdClass; $value->prop = 1', expectedStdout: nowdoc(function () {/*<<<EOS object(stdClass)#1 (1) { ["prop"]=> int(1) } EOS */;}) // jshint ignore:line }, 'stdClass instance with one property containing another stdClass instance': { value: 'new stdClass; $value->sub = new stdClass; $value->sub->prop = 1', expectedStdout: nowdoc(function () {/*<<<EOS object(stdClass)#1 (1) { ["sub"]=> object(stdClass)#2 (1) { ["prop"]=> int(1) } } EOS */;}) // jshint ignore:line }, 'string': { value: '"world"', expectedStdout: 'string(5) "world"\n' }, 'stdClass instance with one property referring to itself': { value: 'new stdClass; $value->me = $value', expectedStdout: nowdoc(function () {/*<<<EOS object(stdClass)#1 (1) { ["me"]=> *RECURSION* } EOS */;}) // jshint ignore:line }, 'array assigned to an element of itself - should be a copy, so no recursion': { value: 'array(); $value[0] = $value', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [0]=> array(0) { } } EOS */;}) // jshint ignore:line }, 'array element with reference to variable': { value: 'array(); $where = "Here"; $value[0] =& $where; $where = "There"', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [0]=> &string(5) "There" } EOS */;}) // jshint ignore:line }, 'reference to variable containing array assigned to an element of itself - should not be a copy, so recurses': { value: 'array(); $value[0] =& $value', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [0]=> &array(1) { [0]=> *RECURSION* } } EOS */;}) // jshint ignore:line }, 'reference to variable containing object assigned to a property of itself': { value: 'new stdClass; $value->prop =& $value', expectedStdout: nowdoc(function () {/*<<<EOS object(stdClass)#1 (1) { ["prop"]=> *RECURSION* } EOS */;}) // jshint ignore:line }, 'circular reference from array -> object -> array': { value: 'array(); $object = new stdClass; $value[0] =& $object; $object->prop =& $value', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [0]=> &object(stdClass)#1 (1) { ["prop"]=> &array(1) { [0]=> *RECURSION* } } } EOS */;}) // jshint ignore:line } }, function (scenario, description) { describe(description, function () { check({ code: '<?php $value = ' + scenario.value + '; var_dump($value);', expectedStderr: scenario.expectedStderr || '', expectedStdout: scenario.expectedStdout }); }); }); });
describe('PHP Engine var_dump() builtin function integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'when given no arguments': { code: '<?php return var_dump();', expectedResult: null, expectedStderr: 'PHP Warning: var_dump() expects at least 1 parameter, 0 given\n', expectedStdout: '' }, 'attempting to dump property of undefined variable': { code: nowdoc(function () {/*<<<EOS <?php var_dump($undefinedVar->prop); echo 'Done'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: nowdoc(function () {/*<<<EOS PHP Notice: Undefined variable: undefinedVar PHP Notice: Trying to get property of non-object EOS */;}), // jshint ignore:line // Note that the 'Done' echo following the dump must be executed, this is only a notice expectedStdout: nowdoc(function () {/*<<<EOS NULL Done EOS */;}) // jshint ignore:line }, 'attempting to dump result of undefined variable method call': { code: nowdoc(function () {/*<<<EOS <?php var_dump($undefinedVar->myMethod()); EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to a member function myMethod\(\) on a non-object$/ }, expectedStderr: nowdoc(function () {/*<<<EOS PHP Notice: Undefined variable: undefinedVar PHP Fatal error: Call to a member function myMethod() on a non-object EOS */;}), // jshint ignore:line expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); describe('when given one argument', function () { _.each({ 'empty array': { value: 'array()', expectedStdout: nowdoc(function () {/*<<<EOS array(0) { } EOS */;}) // jshint ignore:line }, 'array with one element with implicit key': { value: 'array(7)', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [0]=> int(7) } EOS */;}) // jshint ignore:line }, 'array with one element with explicit key': { value: 'array(4 => "a")', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [4]=> string(1) "a" } EOS */;}) // jshint ignore:line }, 'array with one element with explicit key for subarray value': { value: 'array(5 => array(6, 7))', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [5]=> array(2) { [0]=> int(6) [1]=> int(7) } } EOS */;}) // jshint ignore:line }, 'boolean true': { value: 'true', expectedStdout: 'bool(true)\n' }, 'boolean false': { value: 'false', expectedStdout: 'bool(false)\n' }, 'float': { value: '2.2', expectedStdout: 'float(2.2)\n' }, 'integer': { value: '3', expectedStdout: 'int(3)\n' }, 'null': { value: 'null', expectedStdout: 'NULL\n' }, 'empty stdClass instance': { value: 'new stdClass', expectedStdout: nowdoc(function () {/*<<<EOS object(stdClass)#1 (0) { } EOS */;}) // jshint ignore:line }, 'stdClass instance with one property': { value: 'new stdClass; $value->prop = 1', expectedStdout: nowdoc(function () {/*<<<EOS object(stdClass)#1 (1) { ["prop"]=> int(1) } EOS */;}) // jshint ignore:line }, 'stdClass instance with one property containing another stdClass instance': { value: 'new stdClass; $value->sub = new stdClass; $value->sub->prop = 1', expectedStdout: nowdoc(function () {/*<<<EOS object(stdClass)#1 (1) { ["sub"]=> object(stdClass)#2 (1) { ["prop"]=> int(1) } } EOS */;}) // jshint ignore:line }, 'string': { value: '"world"', expectedStdout: 'string(5) "world"\n' }, 'stdClass instance with one property referring to itself': { value: 'new stdClass; $value->me = $value', expectedStdout: nowdoc(function () {/*<<<EOS object(stdClass)#1 (1) { ["me"]=> *RECURSION* } EOS */;}) // jshint ignore:line }, 'array assigned to an element of itself - should be a copy, so no recursion': { value: 'array(); $value[0] = $value', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [0]=> array(0) { } } EOS */;}) // jshint ignore:line }, 'array element with reference to variable': { value: 'array(); $where = "Here"; $value[0] =& $where; $where = "There"', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [0]=> &string(5) "There" } EOS */;}) // jshint ignore:line }, 'reference to variable containing array assigned to an element of itself - should not be a copy, so recurses': { value: 'array(); $value[0] =& $value', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [0]=> &array(1) { [0]=> *RECURSION* } } EOS */;}) // jshint ignore:line }, 'reference to variable containing object assigned to a property of itself': { value: 'new stdClass; $value->prop =& $value', expectedStdout: nowdoc(function () {/*<<<EOS object(stdClass)#1 (1) { ["prop"]=> *RECURSION* } EOS */;}) // jshint ignore:line }, 'circular reference from array -> object -> array': { value: 'array(); $object = new stdClass; $value[0] =& $object; $object->prop =& $value', expectedStdout: nowdoc(function () {/*<<<EOS array(1) { [0]=> &object(stdClass)#1 (1) { ["prop"]=> &array(1) { [0]=> *RECURSION* } } } EOS */;}) // jshint ignore:line } }, function (scenario, description) { describe(description, function () { check({ code: '<?php $value = ' + scenario.value + '; var_dump($value);', expectedStderr: scenario.expectedStderr || '', expectedStdout: scenario.expectedStdout }); }); }); }); });
describe('PHP Engine magic "__autoload" function statement integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'should throw a fatal error if magic __autoload function in global namespace does not take any arguments': { code: nowdoc(function () {/*<<<EOS <?php function __autoload() {} EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: __autoload\(\) must take exactly 1 argument$/ }, expectedStderr: 'PHP Fatal error: __autoload() must take exactly 1 argument', expectedStdout: '' }, 'should throw a fatal error if magic __autoload function with non-lower case in global namespace does not take any arguments': { code: nowdoc(function () {/*<<<EOS <?php function __autoLOAd() {} EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: __autoload\(\) must take exactly 1 argument$/ }, expectedStderr: 'PHP Fatal error: __autoload() must take exactly 1 argument', expectedStdout: '' }, 'should not throw a fatal error if magic __autoload function in a namespace does not take any arguments': { code: nowdoc(function () {/*<<<EOS <?php namespace MyNamespace; function __autoload() {} EOS */;}), // jshint ignore:line expectedResult: null, // Note that no error is raised, as the function is not magic if not in the global namespace expectedStderr: '', expectedStdout: '' }, 'should not be called when no class or interface is used': { code: nowdoc(function () {/*<<<EOS <?php function __autoload($class) { echo 'autoloading'; } EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: '' }, 'should not be called when class used in global namespace is already defined': { code: nowdoc(function () {/*<<<EOS <?php function __autoload($class) { echo 'autoloading'; } class Test {} $object = new Test; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: '' }, 'should be called when undefined class is used, erroring if class is still not defined by autoloader': { code: nowdoc(function () {/*<<<EOS <?php function __autoload($class) { echo 'autoloading ' . $class; } $object = new TeSt; EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Class 'TeSt' not found$/ }, // Note additional check for case preservation in class name string passed to autoloader expectedStderr: 'PHP Fatal error: Class \'TeSt\' not found', expectedStdout: 'autoloading TeSt' }, 'should be called when undefined class is used in a namespace, erroring if class is still not defined by autoloader': { code: nowdoc(function () {/*<<<EOS <?php function __autoload($class) { echo 'autoloading ' . $class; } namespace My\Library; $object = new TeSt; EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Class 'My\\Library\\TeSt' not found$/ }, // Note additional check for case preservation in class name string passed to autoloader expectedStderr: 'PHP Fatal error: Class \'My\\Library\\TeSt\' not found', expectedStdout: 'autoloading My\\Library\\TeSt' }, 'should be called when undefined class is used, not erroring if class is then defined with same case by autoloader': { code: nowdoc(function () {/*<<<EOS <?php function __autoload($class) { class Test {} echo 'autoloaded ' . $class; } $object = new Test; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'autoloaded Test' }, 'should be called when undefined class is used, not erroring if class is then defined with different case by autoloader': { code: nowdoc(function () {/*<<<EOS <?php function __autoload($class) { class MyTESTClass {} echo 'autoloaded ' . $class; } $object = new Mytestclass; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'autoloaded Mytestclass' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine function call operator integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'calling function in global namespace with prefixed path': { code: nowdoc(function () {/*<<<EOS <?php function printIt() { echo 'it'; } \printIt(); EOS */;}), // jshint ignore:line expectedStderr: '', expectedStdout: 'it' }, 'calling function in another deep namespace with prefixed path': { code: nowdoc(function () {/*<<<EOS <?php namespace MyStuff\Tools; function printIt() { echo 'it'; } namespace MyProgram; \MyStuff\Tools\printIt(); EOS */;}), // jshint ignore:line expectedStderr: '', expectedStdout: 'it' }, 'call to function that is defined in current namespace should not fall back to global namespace': { code: nowdoc(function () {/*<<<EOS <?php function printIt() { echo 'global-it'; } namespace MyStuff; function printIt() { echo 'MyStuff-it'; } namespace MyStuff; printIt(); EOS */;}), // jshint ignore:line expectedStderr: '', expectedStdout: 'MyStuff-it' }, 'call to function not defined in current namespace should fall back to global and not a parent namespace': { code: nowdoc(function () {/*<<<EOS <?php function printIt() { echo 'global-it'; } namespace MyStuff; function printIt() { echo 'MyStuff-it'; } namespace MyStuff\Tools; printIt(); EOS */;}), // jshint ignore:line expectedStderr: '', expectedStdout: 'global-it' }, 'call to function defined in current namespace via prefixed string': { code: nowdoc(function () {/*<<<EOS <?php namespace MyTest; function myFunc() { return 24; } $fn = 'MyTest\myFunc'; return $fn(); EOS */;}), // jshint ignore:line expectedResult: 24, expectedResultType: 'integer', expectedStderr: '', expectedStdout: '' }, 'call to instance method via array': { code: nowdoc(function () {/*<<<EOS <?php class MyClass { public function printMsg($msg) { print $msg; return 24; } } $object = new MyClass; $ref = array($object, 'printMsg'); return $ref('it'); EOS */;}), // jshint ignore:line expectedResult: 24, expectedResultType: 'integer', expectedStderr: '', expectedStdout: 'it' }, 'call to static method via array': { code: nowdoc(function () {/*<<<EOS <?php class MyClass { public static function printMsg($msg) { print $msg; return 24; } } $ref = array('MyClass', 'printMsg'); return $ref('it'); EOS */;}), // jshint ignore:line expectedResult: 24, expectedResultType: 'integer', expectedStderr: '', expectedStdout: 'it' }, 'passing an array literal referencing variable directly to a function when calling': { code: nowdoc(function () {/*<<<EOS <?php function getFirst($numbers) { return $numbers[0]; } $seven = 7; $eight = 8; return getFirst(array($seven, '1' => $eight)); EOS */;}), // jshint ignore:line expectedResult: 7, expectedResultType: 'integer', expectedStderr: '', expectedStdout: '' }, 'attempting to call instance method via array with only one element should fail': { code: nowdoc(function () {/*<<<EOS <?php class MyClass { public function printIt() { print 'it'; return 24; } } $object = new MyClass; $ref = array($object); return $ref(); EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Function name must be a string$/ }, expectedStderr: 'PHP Fatal error: Function name must be a string', expectedStdout: '' }, 'call to function defined in current namespace via unprefixed string should fail': { code: nowdoc(function () {/*<<<EOS <?php namespace MyTest; function myFunc() {} $fn = 'myFunc'; $fn(); EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to undefined function myFunc\(\)$/ }, expectedStderr: 'PHP Fatal error: Call to undefined function myFunc()', expectedStdout: '' }, 'call to undefined function in another namespace with prefixed path': { code: nowdoc(function () {/*<<<EOS <?php namespace Test; \Creator\Stuff\doSomething(); EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to undefined function Creator\\Stuff\\doSomething\(\)$/ }, expectedStderr: 'PHP Fatal error: Call to undefined function Creator\\Stuff\\doSomething()', expectedStdout: '' }, 'call to undefined function in another namespace with unprefixed path': { code: nowdoc(function () {/*<<<EOS <?php namespace Test; Creator\Stuff\doSomething(); EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to undefined function Test\\Creator\\Stuff\\doSomething\(\)$/ }, expectedStderr: 'PHP Fatal error: Call to undefined function Test\\Creator\\Stuff\\doSomething()', expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine strtr() builtin function integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ '3-argument form': { code: nowdoc(function () {/*<<<EOS <?php $msg = 'Hello from abc!'; return strtr($msg, 'abcd', 'efgh'); EOS */;}), // jshint ignore:line expectedResult: 'Hello from efg!', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, '3-argument form with longer $from ignores extra characters in $to': { code: nowdoc(function () {/*<<<EOS <?php $msg = 'Hello from abc!'; return strtr($msg, 'abcdefgh', 'ijk'); EOS */;}), // jshint ignore:line expectedResult: 'Hello from ijk!', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, '2-argument form': { code: nowdoc(function () {/*<<<EOS <?php $msg = 'Hello from abc, abc!'; return strtr($msg, array('ello' => 'i', 'b' => 'z')); EOS */;}), // jshint ignore:line expectedResult: 'Hi from azc, azc!', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine do...while statement integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'do...while loop with bool(false) condition - should execute body statements once': { code: nowdoc(function () {/*<<<EOS <?php do { echo 1; echo 2; } while (false); echo 'Done.'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: '12Done.' }, 'do...while loop with bool(false) condition and with no braces around body - should execute body statement once': { code: nowdoc(function () {/*<<<EOS <?php do echo 1; while (false); echo 'Done.'; EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: '1Done.' }, 'do...while loop with counter to only execute 2 times': { code: nowdoc(function () {/*<<<EOS <?php $a = 0; do { echo $a++ . ','; } while ($a < 2); EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: '0,1,' }, 'do...while loop with non-boolean falsy value - countdown from 2 to 0': { code: nowdoc(function () {/*<<<EOS <?php $a = 2; do { echo $a-- . ','; } while ($a); EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: '2,1,' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine double-quoted string construct integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'empty string': { code: nowdoc(function () {/*<<<EOS <?php return ""; EOS */;}), // jshint ignore:line expectedResult: '', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text': { code: nowdoc(function () {/*<<<EOS <?php return "hello world"; EOS */;}), // jshint ignore:line expectedResult: 'hello world', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text and backslash escape': { code: nowdoc(function () {/*<<<EOS <?php return "this backslash \\ should end up as a single backslash"; EOS */;}), // jshint ignore:line expectedResult: 'this backslash \\ should end up as a single backslash', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text and invalid backslash escape with whitespace following': { code: nowdoc(function () {/*<<<EOS <?php return "this standalone backslash \ should end up as a single backslash"; EOS */;}), // jshint ignore:line expectedResult: 'this standalone backslash \\ should end up as a single backslash', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text and invalid backslash escape with character following': { code: nowdoc(function () {/*<<<EOS <?php return "this invalid escape \z should be left alone"; EOS */;}), // jshint ignore:line expectedResult: 'this invalid escape \\z should be left alone', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text and dollar escape': { code: nowdoc(function () {/*<<<EOS <?php return "this escaped dollar \$ should end up as a plain dollar"; EOS */;}), // jshint ignore:line expectedResult: 'this escaped dollar $ should end up as a plain dollar', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text and escaped double-quote': { code: nowdoc(function () {/*<<<EOS <?php return "this escaped quote \" should end up as just a double-quote"; EOS */;}), // jshint ignore:line expectedResult: 'this escaped quote " should end up as just a double-quote', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text and ESC escape': { code: nowdoc(function () {/*<<<EOS <?php return "before \e after"; EOS */;}), // jshint ignore:line expectedResult: 'before \x1B after', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text and form feed escape': { code: nowdoc(function () {/*<<<EOS <?php return "before \f after"; EOS */;}), // jshint ignore:line expectedResult: 'before \f after', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text and linefeed escape': { code: nowdoc(function () {/*<<<EOS <?php return "before \n after"; EOS */;}), // jshint ignore:line expectedResult: 'before \n after', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text and two linefeed escapes': { code: nowdoc(function () {/*<<<EOS <?php return "before \n middle \n after"; EOS */;}), // jshint ignore:line expectedResult: 'before \n middle \n after', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text and carriage return escape': { code: nowdoc(function () {/*<<<EOS <?php return "before \r after"; EOS */;}), // jshint ignore:line expectedResult: 'before \r after', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text and horizontal tab escape': { code: nowdoc(function () {/*<<<EOS <?php return "before \t after"; EOS */;}), // jshint ignore:line expectedResult: 'before \t after', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with plain text and vertical tab escape': { code: nowdoc(function () {/*<<<EOS <?php return "before \v after"; EOS */;}), // jshint ignore:line expectedResult: 'before \v after', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' }, 'string with all escapes and interpolated variable': { code: nowdoc(function () {/*<<<EOS <?php $name = 'Dan'; return "before \n \r \t \v \e \f \\ \ \z \$ \" $name after"; EOS */;}), // jshint ignore:line expectedResult: 'before \n \r \t \v \x1B \f \\ \\ \\z $ " Dan after', expectedResultType: 'string', expectedStderr: '', expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine object access operator "->" instance method integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'call to statically referenced instance method returning value': { code: nowdoc(function () {/*<<<EOS <?php class Test { public function getIt() { return 7; } } $object = new Test; return $object->getIt(); EOS */;}), // jshint ignore:line expectedResult: 7, expectedStderr: '', expectedStdout: '' }, 'call to statically referenced instance method with argument and returning value': { code: nowdoc(function () {/*<<<EOS <?php class Test { public function addOne($number) { return $number + 1; } } $object = new Test; return $object->addOne(3); EOS */;}), // jshint ignore:line expectedResult: 4, expectedStderr: '', expectedStdout: '' }, 'call to dynamically referenced instance method returning value': { code: nowdoc(function () {/*<<<EOS <?php class Test { public function getIt() { return 6; } } $object = new Test; $methodName = 'getIt'; return $object->$methodName(); EOS */;}), // jshint ignore:line expectedResult: 6, expectedStderr: '', expectedStdout: '' }, 'call to undefined method of object when class is in global namespace': { code: nowdoc(function () {/*<<<EOS <?php class Test {} $object = new Test; var_dump($object->iDontExist()); EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to undefined method Test::iDontExist\(\)$/ }, expectedStderr: 'PHP Fatal error: Call to undefined method Test::iDontExist()', expectedStdout: '' }, 'call to undefined method of object when class is in a namespace': { code: nowdoc(function () {/*<<<EOS <?php namespace MyStuff; class Test {} $object = new Test; var_dump($object->iDontExist()); EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to undefined method MyStuff\\Test::iDontExist\(\)$/ }, expectedStderr: 'PHP Fatal error: Call to undefined method MyStuff\\Test::iDontExist()', expectedStdout: '' }, // Ensure we use .hasOwnProperty(...) checks internally 'call to undefined instance method called "constructor"': { code: nowdoc(function () {/*<<<EOS <?php class Earth {} $planet = new Earth; return $planet->constructor(); EOS */;}), // jshint ignore:line expectedException: { instanceOf: PHPFatalError, match: /^PHP Fatal error: Call to undefined method Earth::constructor\(\)$/ }, expectedStderr: 'PHP Fatal error: Call to undefined method Earth::constructor()', expectedStdout: '' }, 'calling static method as instance method': { code: nowdoc(function () {/*<<<EOS <?php class Animal { public static function getPlanet() { return 'Earth'; } } $animal = new Animal(); return $animal->getPlanet(); EOS */;}), // jshint ignore:line expectedResult: 'Earth', expectedResultType: 'string', // Note that no notices are generated at all expectedStderr: '', expectedStdout: '' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });
describe('PHP Engine spl_autoload_unregister() builtin function integration', function () { var engine; function check(scenario) { engineTools.check(function () { return { engine: engine }; }, scenario); } beforeEach(function () { engine = phpTools.createEngine(); }); _.each({ 'returns true when function has been registered': { code: nowdoc(function () {/*<<<EOS <?php function myAutoloader($className) { } spl_autoload_register('myAutoloader'); return spl_autoload_unregister('myAutoloader'); EOS */;}), // jshint ignore:line expectedResult: true, expectedResultType: 'boolean', expectedStderr: '', expectedStdout: '' }, 'returns false when function has not been registered': { code: nowdoc(function () {/*<<<EOS <?php function myAutoloader($className) { } return spl_autoload_unregister('myAutoloader'); EOS */;}), // jshint ignore:line expectedResult: false, expectedResultType: 'boolean', expectedStderr: '', expectedStdout: '' }, 'still allows the previous autoload function to be called': { code: nowdoc(function () {/*<<<EOS <?php function firstAutoloader($className) { class MyClass {} print 'First: ' . $className; } spl_autoload_register('firstAutoloader'); function secondAutoloader($className) { class MyClass {} print 'Second: ' . $className; } spl_autoload_register('secondAutoloader'); spl_autoload_unregister('secondAutoloader'); new MyClass(); EOS */;}), // jshint ignore:line expectedResult: null, expectedStderr: '', expectedStdout: 'First: MyClass' } }, function (scenario, description) { describe(description, function () { check(scenario); }); }); });