V8Js هو امتداد PHP لمحرك Google V8 Javascript.
يتيح لك الامتداد تنفيذ كود Javascript في وضع الحماية الآمن من PHP. يمكن تقييد التعليمات البرمجية المنفذة باستخدام حد زمني و/أو حد للذاكرة. وهذا يوفر إمكانية تنفيذ تعليمات برمجية غير موثوقة بثقة.
مكتبة محرك جافا سكريبت V8 (libv8) الرئيسية https://github.com/v8/v8-git-mirror (trunk)
V8 هو محرك جافا سكريبت مفتوح المصدر من Google. V8 مكتوب بلغة C++ ويستخدم في Google Chrome، المتصفح مفتوح المصدر من Google. يقوم V8 بتنفيذ ECMAScript كما هو محدد في ECMA-262، الإصدار الخامس.
يتطلب هذا الامتداد V8 9.0 أو أعلى.
يتم نشر إصدارات V8 بسرعة كبيرة، وعادةً ما يوفر فريق V8 الدعم الأمني لخط الإصدار الذي يتم شحنه مع متصفح Chrome (القناة الثابتة) والأحدث (فقط). للحصول على نظرة عامة على الإصدار، راجع https://chromiumdash.appspot.com/branches.
PHP 8.0.0+
يستخدم هذا التنفيذ المضمن لمحرك V8 قفل الخيط بحيث يعمل مع تمكين ZTS.
نظام التشغيل Windows غير مدعوم رسميًا حاليًا. في الغالب لأنه ليس لدي الوقت الكافي للحفاظ على الدعم له بنفسي، وليس لدي حقًا صناديق Windows لتجربة الأشياء بها. سيكون أمرًا رائعًا أن يتمكن شخص ما من تكثيف الأمور وإصلاحها على Windows، وتوفير ثنائيات V8 سابقة الإنشاء، وما إلى ذلك.
يوجد فرع اسمه php7
يستهدف PHP 7.0.0+
بالنسبة لبعض الخطوات الأولى، بدلاً من التجميع يدويًا، قد ترغب في تجربة صورة عامل الإرساء V8Js. يحتوي على إصدارات v8 وv8js وphp-cli مثبتة مسبقًا حتى تتمكن من تجربتها مع PHP في "الوضع التفاعلي". ومع ذلك، لا يوجد أباتشي وما إلى ذلك قيد التشغيل.
يعد البناء على Microsoft Windows أكثر تعقيدًا بعض الشيء، راجع ملف README.Win32.md للتشغيل السريع. يعد البناء على GNU/Linux وMacOS X أمرًا سهلاً، راجع ملفات README.Linux.md وREADME.MacOS.md للتعرف على الملاحظات الخاصة بالنظام الأساسي.
<?php
class V8Js
{
/* Constants */
const V8_VERSION = '' ;
const FLAG_NONE = 1 ;
const FLAG_FORCE_ARRAY = 2 ;
const FLAG_PROPAGATE_PHP_EXCEPTIONS = 4 ;
/* Methods */
/**
* Initializes and starts V8 engine and returns new V8Js object with it's own V8 context.
* @param string $object_name
* @param array $variables
* @param string $snapshot_blob
*/
public function __construct ( $ object_name = " PHP " , array $ variables = [], $ snapshot_blob = NULL )
{}
/**
* Provide a function or method to be used to load required modules. This can be any valid PHP callable.
* The loader function will receive the normalised module path and should return Javascript code to be executed.
* @param callable $loader
*/
public function setModuleLoader ( callable $ loader )
{}
/**
* Provide a function or method to be used to normalise module paths. This can be any valid PHP callable.
* This can be used in combination with setModuleLoader to influence normalisation of the module path (which
* is normally done by V8Js itself but can be overriden this way).
* The normaliser function will receive the base path of the current module (if any; otherwise an empty string)
* and the literate string provided to the require method and should return an array of two strings (the new
* module base path as well as the normalised name). Both are joined by a '/' and then passed on to the
* module loader (unless the module was cached before).
* @param callable $normaliser
*/
public function setModuleNormaliser ( callable $ normaliser )
{}
/**
* Provate a function or method to be used to convert/proxy PHP exceptions to JS.
* This can be any valid PHP callable.
* The converter function will receive the PHP Exception instance that has not been caught and
* is due to be forwarded to JS. Pass NULL as $filter to uninstall an existing filter.
*/
public function setExceptionFilter ( callable $ filter )
{}
/**
* Compiles and executes script in object's context with optional identifier string.
* A time limit (milliseconds) and/or memory limit (bytes) can be provided to restrict execution. These options will throw a V8JsTimeLimitException or V8JsMemoryLimitException.
* @param string $script
* @param string $identifier
* @param int $flags
* @param int $time_limit in milliseconds
* @param int $memory_limit in bytes
* @return mixed
*/
public function executeString ( $ script , $ identifier = '' , $ flags = V8Js:: FLAG_NONE , $ time_limit = 0 , $ memory_limit = 0 )
{}
/**
* Compiles a script in object's context with optional identifier string.
* @param $script
* @param string $identifier
* @return resource
*/
public function compileString ( $ script , $ identifier = '' )
{}
/**
* Executes a precompiled script in object's context.
* A time limit (milliseconds) and/or memory limit (bytes) can be provided to restrict execution. These options will throw a V8JsTimeLimitException or V8JsMemoryLimitException.
* @param resource $script
* @param int $flags
* @param int $time_limit
* @param int $memory_limit
*/
public function executeScript ( $ script , $ flags = V8Js:: FLAG_NONE , $ time_limit = 0 , $ memory_limit = 0 )
{}
/**
* Set the time limit (in milliseconds) for this V8Js object
* works similar to the set_time_limit php
* @param int $limit
*/
public function setTimeLimit ( $ limit )
{}
/**
* Set the memory limit (in bytes) for this V8Js object
* @param int $limit
*/
public function setMemoryLimit ( $ limit )
{}
/**
* Set the average object size (in bytes) for this V8Js object.
* V8's "amount of external memory" is adjusted by this value for every exported object. V8 triggers a garbage collection once this totals to 192 MB.
* @param int $average_object_size
*/
public function setAverageObjectSize ( $ average_object_size )
{}
/**
* Returns uncaught pending exception or null if there is no pending exception.
* @return V8JsScriptException|null
*/
public function getPendingException ()
{}
/**
* Clears the uncaught pending exception
*/
public function clearPendingException ()
{}
/** Static methods **/
/**
* Creates a custom V8 heap snapshot with the provided JavaScript source embedded.
* @param string $embed_source
* @return string|false
*/
public static function createSnapshot ( $ embed_source )
{}
}
final class V8JsScriptException extends Exception
{
/**
* @return string
*/
final public function getJsFileName ( ) {}
/**
* @return int
*/
final public function getJsLineNumber ( ) {}
/**
* @return int
*/
final public function getJsStartColumn ( ) {}
/**
* @return int
*/
final public function getJsEndColumn ( ) {}
/**
* @return string
*/
final public function getJsSourceLine ( ) {}
/**
* @return string
*/
final public function getJsTrace ( ) {}
}
final class V8JsTimeLimitException extends Exception
{
}
final class V8JsMemoryLimitException extends Exception
{
}
// Print a string.
print ( string ) ;
// Dump the contents of a variable.
var_dump ( value ) ;
// Terminate Javascript execution immediately.
exit ( ) ;
// CommonJS Module support to require external code.
// This makes use of the PHP module loader provided via V8Js::setModuleLoader (see PHP API above).
require ( "path/to/module" ) ;
عامل التشغيل JavaScript in
، عند تطبيقه على كائن PHP مغلف، يعمل بنفس وظيفة PHP isset()
. وبالمثل، عند تطبيقه على كائن PHP ملفوف، يعمل delete
JavaScript مثل PHP unset
.
<?php
class Foo {
var $ bar = null ;
}
$ v8 = new V8Js ();
$ v8 -> foo = new Foo ;
// This prints "no"
$ v8 -> executeString ( ' print( "bar" in PHP.foo ? "yes" : "no" ); ' );
?>
تحتوي PHP على مساحات أسماء منفصلة للخصائص والأساليب، بينما تحتوي JavaScript على مساحة واحدة فقط. عادة لا تكون هذه مشكلة، ولكن إذا كنت بحاجة إلى ذلك، يمكنك استخدام $
البادئة لتحديد خاصية، أو __call
لاستدعاء طريقة على وجه التحديد.
<?php
class Foo {
var $ bar = " bar " ;
function bar ( $ what ) { echo " I'm a " , $ what , " ! n" ; }
}
$ foo = new Foo ;
// This prints 'bar'
echo $ foo -> bar , "n" ;
// This prints "I'm a function!"
$ foo -> bar ( " function " );
$ v8 = new V8Js ();
$ v8 -> foo = new Foo ;
// This prints 'bar'
$ v8 -> executeString ( ' print(PHP.foo.$bar, "n"); ' );
// This prints "I'm a function!"
$ v8 -> executeString ( ' PHP.foo.__call("bar", ["function"]); ' );
?>
أنواع بيانات PHP وJavaScript غير متطابقة تمامًا. هذا بالطبع كلتا اللغتين لديهما أنواع بيانات للتعامل مع الأرقام. ومع ذلك، فإن PHP تفرق بين الأعداد الصحيحة وأرقام الفاصلة العائمة، على عكس JavaScript التي تحتوي فقط على النوع Number
، وهو رقم النقطة العائمة IEEE 754. في كثير من الحالات، لا يهم هذا على الإطلاق، حيث يمكن لكلتا اللغتين تمثيل نفس الرقم بشكل جيد. ولكن هناك حالات الحافة.
في أنظمة 64 بت، تسمح PHP للأعداد الصحيحة بأن تحتوي على 64 بتة مهمة، بينما يحتوي نوع رقم JavaScripts (أي IEEE 754) على 52 بتة فقط. ومن ثم سيتم فقدان بعض الدقة. يصبح هذا مهمًا إذا قمت بتمرير قيم عددية تحتوي على أكثر من 15 رقمًا عشريًا دقيقًا.
على الرغم من الاسم الشائع، فإن مفهوم المصفوفات يختلف تمامًا بين PHP وJavaScript. في جافا سكريبت، المصفوفة عبارة عن مجموعة متجاورة من العناصر المفهرسة بأرقام متكاملة من الصفر إلى الأعلى. في PHP، يمكن أن تكون المصفوفات متفرقة، أي أن المفاتيح المتكاملة لا يلزم أن تكون متجاورة وقد تكون سالبة. بالإضافة إلى ذلك، لا يجوز لمصفوفات PHP استخدام الأرقام الصحيحة كمفاتيح فحسب، بل أيضًا سلاسل (ما يسمى بالمصفوفات الترابطية). تسمح مصفوفات JavaScript المعاكسة بإرفاق الخصائص بالمصفوفات، وهو ما لا تدعمه PHP. هذه الخصائص ليست جزءًا من مجموعة المصفوفات، على سبيل المثال طريقة Array.prototype.forEach
لا "ترى" هذه الخصائص.
بشكل عام، يتم تعيين مصفوفات PHP إلى مصفوفات JavaScript "الأصلية" إذا كان ذلك ممكنًا، أي أن مصفوفة PHP تستخدم مفاتيح رقمية متجاورة من الصفر فصاعدًا. يتم تعيين كل من المصفوفات الترابطية والمتفرقة إلى كائنات JavaScript. تحتوي هذه الكائنات على مُنشئ يُسمى أيضًا "Array"، ولكنها ليست مصفوفات أصلية ولا تشترك في Array.prototype، وبالتالي فهي لا تدعم (مباشرة) وظائف المصفوفة النموذجية مثل join
و forEach
وما إلى ذلك. مصفوفات PHP هي تصدير القيمة على الفور حسب القيمة دون الربط المباشر. هذا إذا قمت بتغيير قيمة على جانب JavaScript أو دفعت المزيد من القيم إلى المصفوفة، فلن ينعكس هذا التغيير على جانب PHP.
إذا تم تمرير مصفوفات JavaScript مرة أخرى إلى PHP، فسيتم دائمًا تحويل مصفوفة JavaScript إلى مصفوفة PHP. إذا كانت مصفوفة JavaScript تحتوي على خصائص (خاصة) مرفقة، فسيتم تحويلها أيضًا إلى مفاتيح مصفوفة PHP.
يتم تعيين كائنات PHP التي تم تمريرها إلى JavaScript إلى كائنات JavaScript الأصلية التي لها وظيفة إنشاء "افتراضية" تحمل اسم فئة كائن PHP. يمكن استخدام وظيفة المنشئ هذه لإنشاء مثيلات جديدة لفئة PHP طالما أن فئة PHP لا تحتوي على طريقة __construct
غير عامة. تكون كافة الأساليب والخصائص العامة مرئية لرمز JavaScript وتكون الخصائص مرتبطة مباشرة، على سبيل المثال، إذا تم تغيير قيمة الخاصية بواسطة رمز JavaScript، فإن كائن PHP يتأثر أيضًا.
إذا تم تمرير كائن JavaScript أصلي إلى PHP، فسيتم تعيين كائن JavaScript إلى كائن PHP من فئة V8Object
. يحتوي هذا الكائن على كافة الخصائص التي يمتلكها كائن JavaScript وهو قابل للتغيير بالكامل. إذا تم تعيين دالة لإحدى هذه الخصائص، فيمكن استدعاؤها أيضًا بواسطة كود PHP. يمكن تكوين وظيفة executeString
لتعيين كائنات JavaScript دائمًا إلى صفائف PHP عن طريق تعيين علامة V8Js::FLAG_FORCE_ARRAY
. ثم ينطبق سلوك المصفوفة القياسية على أن القيم ليست مرتبطة مباشرة، أي إذا قمت بتغيير قيم مصفوفة PHP الناتجة، فلن يتأثر كائن JavaScript.
تنطبق القاعدة المذكورة أعلاه والتي تنص على تحويل كائنات PHP بشكل عام إلى كائنات JavaScript أيضًا على كائنات PHP من نوع ArrayObject
أو الفئات الأخرى، التي تنفذ كلاً من ArrayAccess
والواجهة Countable
- على الرغم من أنها تتصرف مثل مصفوفات PHP.
يمكن تغيير هذا السلوك عن طريق تمكين علامة php.ini v8js.use_array_access
. إذا تم تعيينها، فسيتم تحويل كائنات فئات PHP التي تنفذ الواجهات المذكورة أعلاه إلى كائنات تشبه JavaScript Array. يؤدي الوصول إلى هذا الكائن حسب الفهرس إلى استدعاءات فورية لطرق offsetGet
أو offsetSet
PHP (يعد هذا فعليًا ربطًا مباشرًا لـ JavaScript مقابل كائن PHP). يدعم كائن Array-esque هذا أيضًا استدعاء كل طريقة عامة مرفقة لكائن PHP + أساليب أساليب Array.prototype الأصلية لـ JavaScript (طالما لم يتم تحميلها بشكل زائد بواسطة أساليب PHP).
أولاً وقبل كل شيء، تعد لقطات بدء التشغيل المخصصة إحدى الميزات التي يوفرها V8 نفسه، وهي مبنية فوق ميزة لقطات الكومة العامة الخاصة به. الفكرة هي أنه نظرًا لأنه من الشائع جدًا تحميل بعض مكتبات JavaScript قبل القيام بأي عمل فعلي، فإن كود المكتبة هذا يتم أيضًا تخزينه في لقطة الكومة.
يوفر هذا الامتداد طريقة سهلة لإنشاء تلك اللقطات المخصصة. من أجل إنشاء مثل هذه اللقطة باستخدام دالة fibonacci
المضمنة فيها، ما عليك سوى الاتصال بـ V8Js::createSnapshot
بشكل ثابت مثل هذا:
$ snapshot = V8Js:: createSnapshot ( ' var fibonacci = n => n < 3 ? 1 : fibonacci(n - 1) + fibonacci(n - 2) ' );
ثم احتفظ بمحتويات $snapshot
إلى أي مكان تريده، على سبيل المثال، نظام الملفات المحلي أو ربما Redis.
إذا كنت بحاجة إلى إنشاء مثيل V8Js جديد، فما عليك سوى تمرير اللقطة كوسيط خامس إلى مُنشئ V8Js:
$ jscript = new V8Js ( ' php ' , array (), array (), true , $ snapshot );
echo $ jscript -> executeString ( ' fibonacci(43) ' ) . "n" ;
ضع في اعتبارك أن الكود الذي سيتم تضمينه في اللقطة قد لا يستدعي مباشرة أي من الوظائف المصدرة من PHP، حيث يتم إضافتها مباشرة بعد تشغيل كود اللقطة.
إذا تم إطلاق تعليمات JavaScript البرمجية (بدون التقاطها)، أو تسببت في حدوث أخطاء أو لم يتم تجميعها، فسيتم طرح استثناءات V8JsScriptException
.
لا تتم إعادة طرح استثناءات PHP التي تحدث بسبب الاستدعاءات من كود JavaScript افتراضيًا في سياق JavaScript ولكنها تتسبب في إيقاف تنفيذ JavaScript على الفور ثم يتم الإبلاغ عنها في الموقع الذي يستدعي كود JS.
يمكن تغيير هذا السلوك عن طريق تعيين علامة FLAG_PROPAGATE_PHP_EXCEPTIONS
. إذا تم تعيينه، فسيتم تحويل استثناء PHP (الكائنات) إلى كائنات JavaScript تتوافق مع القواعد المذكورة أعلاه وإعادة طرحها في سياق JavaScript. إذا لم يتم اكتشافها بواسطة كود JavaScript، فسيتوقف التنفيذ ويتم طرح V8JsScriptException
، والذي يحتوي على استثناء PHP الأصلي الذي يمكن الوصول إليه عبر طريقة getPrevious
.
ضع في اعتبارك أن كود JS لديه حق الوصول إلى أساليب مثل getTrace
على كائن الاستثناء. قد يكون هذا سلوكًا غير مرغوب فيه، إذا قمت بتنفيذ تعليمات برمجية غير موثوقة. باستخدام طريقة setExceptionFilter
، يمكن توفير قابل للاستدعاء، والذي قد يحول استثناء PHP إلى قيمة أخرى يمكن كشفها بشكل آمن. قد يقرر عامل التصفية أيضًا عدم نشر الاستثناء إلى JS على الإطلاق إما عن طريق إعادة طرح الاستثناء الذي تم تمريره أو طرح استثناء آخر.