├── .clang_complete ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── core.cpp ├── core.h ├── exception.h ├── extension.cpp ├── fromiterator.cpp ├── fromiterator.h ├── fromphp.cpp ├── fromphp.h ├── isolate.cpp ├── isolate.h ├── link.h ├── linker.cpp ├── linker.h ├── names.h ├── php-js.ini ├── php_array.h ├── php_base.cpp ├── php_base.h ├── php_context.cpp ├── php_context.h ├── php_exception.h ├── php_function.cpp ├── php_function.h ├── php_iterator.cpp ├── php_iterator.h ├── php_object.cpp ├── php_object.h ├── php_script.h ├── php_variable.cpp ├── php_variable.h ├── platform.cpp ├── platform.h ├── scope.h ├── script.cpp ├── script.h ├── template.cpp ├── template.h ├── tests ├── input1.js ├── object.js ├── object.php ├── test1.php ├── test2.php ├── test3.php ├── test4.php └── test5.php ├── timeout.h └── version.sh /.clang_complete: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | -Wno-pragma-once-outside-header 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.d 4 | *_blob.bin 5 | *_blob.h -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile template 3 | # 4 | # This is an example Makefile that can be used by anyone who is building 5 | # his or her own PHP extensions using the PHP-CPP library. 6 | # 7 | # In the top part of this file we have included variables that can be 8 | # altered to fit your configuration, near the bottom the instructions and 9 | # dependencies for the compiler are defined. The deeper you get into this 10 | # file, the less likely it is that you will have to change anything in it. 11 | # 12 | 13 | # 14 | # Name of your extension 15 | # 16 | # This is the name of your extension. Based on this extension name, the 17 | # name of the library file (name.so) and the name of the config file (name.ini) 18 | # are automatically generated 19 | # 20 | 21 | NAME = php-js 22 | 23 | 24 | # 25 | # Php.ini directories 26 | # 27 | # In the past, PHP used a single php.ini configuration file. Today, most 28 | # PHP installations use a conf.d directory that holds a set of config files, 29 | # one for each extension. Use this variable to specify this directory. 30 | # 31 | # 32 | 33 | INI_DIR = $(shell php-config --ini-path)/../mods-available 34 | 35 | # 36 | # The extension dirs 37 | # 38 | # This is normally a directory like /usr/lib/php/20121221 (based on the 39 | # PHP version that you use. We make use of the command line 'php-config' 40 | # instruction to find out what the extension directory is, you can override 41 | # this with a different fixed directory 42 | # 43 | 44 | EXTENSION_DIR = $(shell php-config --extension-dir) 45 | 46 | 47 | # 48 | # The name of the extension and the name of the .ini file 49 | # 50 | # These two variables are based on the name of the extension. We simply add 51 | # a certain extension to them (.so or .ini) 52 | # 53 | 54 | EXTENSION = ${NAME}.so 55 | INI = ${NAME}.ini 56 | 57 | 58 | # 59 | # Compiler 60 | # 61 | # By default, the GNU C++ compiler is used. If you want to use a different 62 | # compiler, you can change that here. You can change this for both the 63 | # compiler (the program that turns the c++ files into object files) and for 64 | # the linker (the program that links all object files into the single .so 65 | # library file. By default, g++ (the GNU C++ compiler) is used for both. 66 | # 67 | 68 | COMPILER = c++ 69 | LINKER = c++ 70 | 71 | 72 | # 73 | # Compiler and linker flags 74 | # 75 | # - V8_COMPRESS_POINTERS is needed because the V8 library is compiled with a special optimization for pointers 76 | # - V8_ENABLE_SANDBOX is needed because the V8 library is compiled with sandbox support 77 | # 78 | 79 | COMPILER_FLAGS = -Wall -c -O2 -MD -std=c++20 -fpic -DVERSION="`./version.sh`" -DV8_COMPRESS_POINTERS -DV8_ENABLE_SANDBOX -I. -g 80 | LINKER_FLAGS = -shared 81 | LINKER_DEPENDENCIES = -Wl,--no-as-needed -lphpcpp -lv8_libplatform -lv8 82 | 83 | 84 | # 85 | # Command to remove files, copy files and create directories. 86 | # 87 | # I've never encountered a *nix environment in which these commands do not work. 88 | # So you can probably leave this as it is 89 | # 90 | 91 | RM = rm -f 92 | CP = cp -f 93 | MKDIR = mkdir -p 94 | XXD = xxd -i 95 | 96 | 97 | # 98 | # All source files are simply all *.cpp files found in the current directory 99 | # 100 | # A builtin Makefile macro is used to scan the current directory and find 101 | # all source files. The object files are all compiled versions of the source 102 | # file, with the .cpp extension being replaced by .o. 103 | # 104 | 105 | SOURCES = $(wildcard *.cpp) 106 | OBJECTS = $(SOURCES:%.cpp=%.o) 107 | DEPENDENCIES = $(SOURCES:%.cpp=%.d) 108 | 109 | 110 | # 111 | # From here the build instructions start 112 | # 113 | 114 | all: ${OBJECTS} ${EXTENSION} 115 | 116 | # 117 | # Use dependency tracking 118 | # 119 | -include ${DEPENDENCIES} 120 | 121 | 122 | ${EXTENSION}: ${OBJECTS} 123 | ${LINKER} ${LINKER_FLAGS} -o $@ ${OBJECTS} ${LINKER_DEPENDENCIES} 124 | 125 | ${OBJECTS}: 126 | ${COMPILER} ${COMPILER_FLAGS} -o $@ ${@:%.o=%.cpp} 127 | 128 | install: 129 | ${CP} ${EXTENSION} ${EXTENSION_DIR} 130 | ${CP} ${INI} ${INI_DIR} 131 | 132 | clean: 133 | ${RM} ${EXTENSION} ${OBJECTS} ${DEPENDENCIES} 134 | 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP-JS 2 | 3 | A library to integrate the Google V8 Javascript Engine in PHP 4 | 5 | ## ABOUT 6 | 7 | The PHP-JS library is created and maintained by [Copernica](https://www.copernica.com). This extension 8 | gives you the power to execute javascript right from your PHP script. 9 | This javascript code is executed using the Google V8 engine - the same engine 10 | that powers the Google Chrome browser and Node.js. 11 | 12 | A typical use case is server-side execution of user-defined JavaScript, for example in 13 | applications that allow end users to write custom scripts or automation logic. 14 | 15 | ## HOW DOES IT WORK? 16 | 17 | PHP-JS allows you to execute JavaScript from PHP code, and expose PHP variables 18 | and functions to a Javascript environment. 19 | 20 | ``` 21 | // create a javascript context 22 | $context = new JS\Context(); 23 | 24 | // assign a javascript function to show a variable 25 | $context->assign('show', function($data) { 26 | var_dump($data); 27 | }); 28 | 29 | // assign a function to summarize 30 | $context->assign('add', function($x, $y) { 31 | return $x + $y; 32 | }); 33 | 34 | // assign some additional variables 35 | $context->assign('x', 10); 36 | $context->assign('y', 'abc'); 37 | $context->assign('z', new DateTime()); 38 | 39 | // evaluate a javscript 40 | try 41 | { 42 | $context->evaluate("show(x); show(y); show(z);"); 43 | } 44 | catch (Exception $exception) 45 | { 46 | echo("Error: ".$exception->getMessage()."\n"); 47 | } 48 | ``` 49 | 50 | PHP-JS can map PHP variables to JS variables and vica versa. Scalars, arrays, objects, 51 | and traversable structures (like iterators) are converted between both environments. Note, 52 | however, that due to fundamental differences between Javascript and PHP, not all 53 | constructs can be 1-to-1 mapped. It is recommended to stick to straightforward 54 | data types and structures. 55 | 56 | 57 | ## DEPENDENCIES 58 | 59 | PHP-JS relies on [PHP-CPP](http://www.php-cpp.com) and the [Google V8 engine](https://v8.dev/). 60 | It is known to work with V8 version 13.6. We recommend compiling V8 using GCC and your system 61 | libraries, rather than the ones bundled with V8. This avoids conflicts whtn installing PHP-JS 62 | as a PHP extension, as PHP itself also uses system libraries. 63 | 64 | Use the following flags when building V8: 65 | 66 | ``` 67 | is_debug=false 68 | is_component_build=true 69 | v8_enable_i18n_support=false 70 | is_clang=false 71 | use_sysroot=false 72 | use_custom_libcxx=false 73 | ``` 74 | 75 | These flags ensure: 76 | 77 | - GCC is used instead of Clang (`is_clang=false`) 78 | - System libraries are used instead of bundled versions (`use_custom_libcxx=false`) 79 | - Unnecessary features like ICU (internationalization) are disabled for a leaner build 80 | 81 | For more details on building V8, visit [https://v8.dev/docs/build-gn](https://v8.dev/docs/build-gn). 82 | 83 | 84 | ## EXTRA INFO 85 | 86 | If you appreciate our work and are you looking for other high quality solutions, take 87 | a look at: 88 | 89 | * PHP-CPP (www.php-cpp.com) 90 | * Copernica Marketing Suite (www.copernica.com) 91 | * MailerQ MTA (www.mailerq.com) 92 | -------------------------------------------------------------------------------- /core.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Core.cpp 3 | * 4 | * Implementation file for the context class 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2025 Copernica BV 8 | */ 9 | 10 | /** 11 | * Dependencies 12 | */ 13 | #include "core.h" 14 | #include "fromphp.h" 15 | #include "php_variable.h" 16 | #include "scope.h" 17 | #include "php_object.h" 18 | #include "php_exception.h" 19 | #include "script.h" 20 | #include "names.h" 21 | 22 | /** 23 | * Begin of namespace 24 | */ 25 | namespace JS { 26 | 27 | /** 28 | * Constructor 29 | */ 30 | Core::Core() : _platform(Platform::instance()), _isolate(this) 31 | { 32 | // when we access the isolate, we need a scope 33 | v8::HandleScope scope(_isolate); 34 | 35 | // create a context 36 | v8::Local context(v8::Context::New(_isolate)); 37 | 38 | // we want to persist the context 39 | _context.Reset(_isolate, context); 40 | 41 | // symbol for linking js and php objects together 42 | v8::Local key = v8::Private::ForApi(_isolate, v8::String::NewFromUtf8Literal(_isolate, "js2php")); 43 | 44 | // persist the key 45 | _symbol.Reset(_isolate, key); 46 | } 47 | 48 | /** 49 | * Wrap a certain PHP object into a javascript object 50 | * @param object MUST be an array or object! 51 | * @return v8::Local 52 | */ 53 | v8::Local Core::wrap(const Php::Value &object) 54 | { 55 | // if the object is already known to be a JS\Object 56 | auto *instance = PhpBase::unwrap(this, object); 57 | 58 | // was this possible? then we reuse the original handle 59 | if (instance != nullptr) return instance->handle(); 60 | 61 | // check the prototypes that we have 62 | for (auto &prototype : _templates) 63 | { 64 | // is this one compatible with the object 65 | if (!prototype->matches(object)) continue; 66 | 67 | // we can apply this prototype 68 | return prototype->apply(object); 69 | } 70 | 71 | // we need a new template 72 | _templates.emplace_back(new Template(_isolate, object)); 73 | 74 | // use it 75 | return _templates.back()->apply(object); 76 | } 77 | 78 | /** 79 | * Assign a variable to the javascript context 80 | * @param name name of property to assign required 81 | * @param value value to be assigned 82 | * @param attribytes property attributes 83 | * @return Php::Value 84 | */ 85 | Php::Value Core::assign(const Php::Value &name, const Php::Value &value, const Php::Value &attributes) 86 | { 87 | // avoid that other contexts are assigned 88 | if (value.instanceOf(Names::Context) || value.instanceOf(Names::Script)) return false; 89 | 90 | // scope for the context 91 | Scope scope(shared_from_this()); 92 | 93 | // retrieve the global object from the context 94 | v8::Local global(scope.global()); 95 | 96 | // the attribute for the newly assigned property 97 | auto attribute = attributes.isNull() ? v8::None : static_cast(attributes.numericValue()); 98 | 99 | // convert the property to a javascript name 100 | v8::Local property = FromPhp(_isolate, name.clone(Php::Type::String)); 101 | 102 | // store the value 103 | v8::Maybe result = global->DefineOwnProperty(scope, property.As(), FromPhp(_isolate, value), attribute); 104 | 105 | // check for success 106 | return result.IsJust() && result.FromJust(); 107 | } 108 | 109 | /** 110 | * Parse a piece of javascript code 111 | * @param source the code to execute 112 | * @param timeout possible timeout in seconds 113 | * @return Php::Value 114 | * @throws Php::Exception 115 | */ 116 | Php::Value Core::evaluate(const Php::Value &source, const Php::Value &timeout) 117 | { 118 | // create a script 119 | Script script(shared_from_this(), source.clone(Php::Type::String).rawValue()); 120 | 121 | // evaluate the script 122 | return script.execute(timeout); 123 | } 124 | 125 | /** 126 | * End of namespace 127 | */ 128 | } 129 | 130 | -------------------------------------------------------------------------------- /core.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Core.h 3 | * 4 | * The main javascript class, used for assigning variables 5 | * and executing javascript 6 | * 7 | * @copyright 2015 - 2025 Copernica B.V. 8 | */ 9 | 10 | /** 11 | * Include guard 12 | */ 13 | #pragma once 14 | 15 | /** 16 | * Dependencies 17 | */ 18 | #include 19 | #include "platform.h" 20 | #include "isolate.h" 21 | #include "template.h" 22 | 23 | /** 24 | * Start namespace 25 | */ 26 | namespace JS { 27 | 28 | /** 29 | * Class definition 30 | */ 31 | class Core : public std::enable_shared_from_this 32 | { 33 | private: 34 | /** 35 | * Pointer to the full javascript v8 platform 36 | * @var Platform 37 | */ 38 | Platform *_platform; 39 | 40 | /** 41 | * The Isolate that manages the v8 environment (this is a bit like the "window" 42 | * platform in a browser, a fully separated environment) 43 | * @var Isolate 44 | */ 45 | Isolate _isolate; 46 | 47 | /** 48 | * The context in which variables are stored 49 | * @var v8::Global 50 | */ 51 | v8::Global _context; 52 | 53 | /** 54 | * The private symbol that we use for associating php objects to js objects 55 | * @var v8::Global 56 | */ 57 | v8::Global _symbol; 58 | 59 | /** 60 | * Templates for wrapping objects 61 | * @var std::vector 62 | */ 63 | std::vector> _templates; 64 | 65 | public: 66 | /** 67 | * Constructor 68 | */ 69 | Core(); 70 | 71 | /** 72 | * No copying allowed 73 | * @param that the object we cannot copy 74 | */ 75 | Core(const Core &that) = delete; 76 | 77 | /** 78 | * Destructor 79 | */ 80 | virtual ~Core() = default; 81 | 82 | /** 83 | * Given an isolate, it is possible to upgrade to the full context 84 | * @return std::shared_ptr 85 | */ 86 | static std::shared_ptr upgrade(v8::Isolate *isolate) 87 | { 88 | return Isolate::core(isolate)->shared_from_this(); 89 | } 90 | 91 | /** 92 | * The isolate of this context 93 | * @return Isolate 94 | */ 95 | v8::Isolate *isolate() { return _isolate; } 96 | 97 | /** 98 | * The symbol used for linkin php and js objects to each other 99 | * @return v8::Global 100 | */ 101 | const v8::Global &symbol() const { return _symbol; } 102 | 103 | /** 104 | * Wrap a certain PHP object into a javascript object 105 | * @param object MUST be an object! 106 | * @return v8::Local 107 | */ 108 | v8::Local wrap(const Php::Value &object); 109 | 110 | /** 111 | * Expose the context 112 | * Watch out: a handling-scope must be passed to prove that is exists 113 | * @param scope 114 | * @return v8::Local 115 | */ 116 | v8::Local context(const v8::HandleScope &scope) { return _context.Get(_isolate); } 117 | 118 | /** 119 | * Assign a variable to the javascript context 120 | * @param name name of property to assign required 121 | * @param value value to be assigned 122 | * @param attribytes property attributes 123 | * @return Php::Value 124 | */ 125 | Php::Value assign(const Php::Value &name, const Php::Value &value, const Php::Value &attributes); 126 | 127 | /** 128 | * Parse a piece of javascript code 129 | * @param code the code to execute 130 | * @param timeout possible timeout in seconds 131 | * @return Php::Value 132 | * @throws Php::Exception 133 | */ 134 | Php::Value evaluate(const Php::Value &code, const Php::Value &timeout); 135 | }; 136 | 137 | /** 138 | * End namespace 139 | */ 140 | } 141 | -------------------------------------------------------------------------------- /exception.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Exception.h 3 | * 4 | * Utility class to turn a PHP space exception into a JS exception 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2025 Copernica BV 8 | */ 9 | 10 | /** 11 | * Include guard 12 | */ 13 | #pragma once 14 | 15 | /** 16 | * Begin of namespace 17 | */ 18 | namespace JS { 19 | 20 | /** 21 | * Class definition 22 | */ 23 | class Exception : public v8::Local 24 | { 25 | public: 26 | /** 27 | * Constructor 28 | * @param isolate 29 | * @param exception 30 | */ 31 | Exception(v8::Isolate *isolate, const Php::Exception &exception) : 32 | v8::Local(v8::Exception::Error(v8::String::NewFromUtf8(isolate, exception.what()).ToLocalChecked())) {} 33 | }; 34 | 35 | /** 36 | * End of namespace 37 | */ 38 | } 39 | 40 | -------------------------------------------------------------------------------- /extension.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * extension.cpp 3 | * 4 | * Startup file for the PHP extension 5 | * 6 | * @copyright 2015 - 2025 Copernica BV 7 | */ 8 | 9 | /** 10 | * Dependencies 11 | */ 12 | #include 13 | #include "php_context.h" 14 | #include "php_object.h" 15 | #include "php_function.h" 16 | #include "php_script.h" 17 | #include "platform.h" 18 | #include "names.h" 19 | 20 | /** 21 | * The VERSION macro is going to be used as string with surrounded quotes 22 | */ 23 | #define STR_VALUE(arg) #arg 24 | #define VERSION_NAME(name) STR_VALUE(name) 25 | #define THE_VERSION VERSION_NAME(VERSION) 26 | 27 | /** 28 | * tell the compiler that the get_module is a pure C function 29 | */ 30 | extern "C" { 31 | 32 | /** 33 | * Function that is called by PHP right after the PHP process 34 | * has started, and that returns an address of an internal PHP 35 | * strucure with all the details and features of your extension 36 | * 37 | * @return void* a pointer to an address that is understood by PHP 38 | */ 39 | PHPCPP_EXPORT void *get_module() 40 | { 41 | // static(!) Php::Extension object that should stay in memory 42 | // for the entire duration of the process (that's why it's static) 43 | static Php::Extension extension("PHP-JS2", THE_VERSION); 44 | 45 | // declare the accessor attributes 46 | // @todo use constants 47 | extension.add(Php::Constant(JS::Names::None, v8::None)); 48 | extension.add(Php::Constant(JS::Names::ReadOnly, v8::ReadOnly)); 49 | extension.add(Php::Constant(JS::Names::DontDelete, v8::DontDelete)); 50 | extension.add(Php::Constant(JS::Names::DontEnumerate, v8::DontEnum)); 51 | 52 | // create the classes 53 | Php::Class context(JS::Names::Context); 54 | Php::Class script(JS::Names::Script); 55 | Php::Class object(JS::Names::Object); 56 | Php::Class function(JS::Names::Function); 57 | 58 | // properties can be assigned to the context 59 | context.method<&JS::PhpContext::assign>("assign", { 60 | Php::ByVal("name", Php::Type::String, true), 61 | Php::ByVal("value", Php::Type::Null, true), 62 | Php::ByVal("attribute", Php::Type::Numeric, false) 63 | }); 64 | 65 | // add a method to parse + execute some script 66 | context.method<&JS::PhpContext::evaluate>("evaluate", { 67 | Php::ByVal("script", Php::Type::String, true), 68 | Php::ByVal("timeout", Php::Type::Numeric, false) 69 | }); 70 | 71 | // add a script-method to construct the script 72 | script.method<&JS::PhpScript::__construct>("__construct", { 73 | Php::ByVal("script", Php::Type::String, true), 74 | }); 75 | 76 | // add a script-method to assign 77 | script.method<&JS::PhpScript::assign>("assign", { 78 | Php::ByVal("name", Php::Type::String, true), 79 | Php::ByVal("value", Php::Type::Null, true), 80 | Php::ByVal("attribute", Php::Type::Numeric, false) 81 | }); 82 | 83 | // add a script-method to execute 84 | script.method<&JS::PhpScript::execute>("execute", { 85 | Php::ByVal("timeout", Php::Type::Numeric, false) 86 | }); 87 | 88 | // add the classes to the extension 89 | extension.add(std::move(context)); 90 | extension.add(std::move(object)); 91 | extension.add(std::move(function)); 92 | 93 | // the platform needs to be cleaned up on engine shutdown 94 | extension.onShutdown([]{ 95 | 96 | // clean up the platform 97 | JS::Platform::shutdown(); 98 | }); 99 | 100 | // return the extension 101 | return extension; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /fromiterator.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * FromIterator.cpp 3 | * 4 | * Implementation file of the FromIterator class 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2025 Copernica BV 8 | */ 9 | 10 | /** 11 | * Dependencies 12 | */ 13 | #include "fromiterator.h" 14 | #include "scope.h" 15 | 16 | /** 17 | * Begin of namespace 18 | */ 19 | namespace JS { 20 | 21 | /** 22 | * Constructor 23 | * @param isolate the isolate 24 | * @param object the object that will be returned 25 | * @param value iterable php object 26 | */ 27 | FromIterator::FromIterator(v8::Isolate *isolate, const Php::Value &value) 28 | { 29 | // we need a scope 30 | Scope scope(isolate); 31 | 32 | // we need the context for looking up the symbol 33 | auto core = Core::upgrade(isolate); 34 | 35 | // create a new object 36 | _iterator = v8::Object::New(isolate); 37 | 38 | // store pointer to state data 39 | _iterator->SetPrivate(scope, core->symbol().Get(isolate), v8::External::New(isolate, new Data(isolate, value))); 40 | 41 | // we need to set a "next" method and a "return" method on the iterator 42 | auto nxtlabel = v8::String::NewFromUtf8Literal(isolate, "next"); 43 | auto retlabel = v8::String::NewFromUtf8Literal(isolate, "return"); 44 | 45 | // we need the next and return methods 46 | auto nxtmethod = v8::Function::New(scope, &FromIterator::nxtmethod).ToLocalChecked(); 47 | auto retmethod = v8::Function::New(scope, &FromIterator::retmethod).ToLocalChecked(); 48 | 49 | // install them on the object 50 | _iterator->Set(scope, nxtlabel, nxtmethod).Check(); 51 | _iterator->Set(scope, retlabel, retmethod).Check(); 52 | 53 | // we also make the iterator of itself iterable (when the iterator is explicitly iterated) 54 | _iterator->Set(scope, v8::Symbol::GetIterator(isolate), _iterator).Check(); 55 | } 56 | 57 | /** 58 | * Helper method to get access to underlying data 59 | * @param isolate 60 | * @param obj 61 | * @return Data* 62 | */ 63 | FromIterator::Data *FromIterator::restore(v8::Isolate *isolate, const v8::Local &obj) 64 | { 65 | // we need the symbol for linking pointers to objects 66 | auto symbol = Core::upgrade(isolate)->symbol().Get(isolate); 67 | 68 | // we need a local value 69 | v8::Local val; 70 | 71 | // get the symbol value 72 | if (!obj->GetPrivate(isolate->GetCurrentContext(), symbol).ToLocal(&val)) return nullptr; 73 | 74 | // should be external 75 | if (!val->IsExternal()) return nullptr; 76 | 77 | // get the value back 78 | return static_cast(val.As()->Value()); 79 | } 80 | 81 | /** 82 | * Helper method to destruct the underlying data 83 | * @param isolate 84 | * @param obj 85 | * @return Data* 86 | */ 87 | void FromIterator::destruct(v8::Isolate *isolate, const v8::Local &obj) 88 | { 89 | // get the underlying data 90 | auto *data = restore(isolate, obj); 91 | 92 | // do nothing if not needed 93 | if (data == nullptr) return; 94 | 95 | // destruct the object 96 | delete data; 97 | 98 | // we need the symbol for linking pointers to objects 99 | auto symbol = Core::upgrade(isolate)->symbol().Get(isolate); 100 | 101 | // unset the symbol value 102 | obj->DeletePrivate(isolate->GetCurrentContext(), symbol); 103 | } 104 | 105 | /** 106 | * Method that is called by v8 when the next item is requested 107 | * @param args 108 | */ 109 | void FromIterator::nxtmethod(const v8::FunctionCallbackInfo &args) 110 | { 111 | // the current isolate 112 | auto isolate = args.GetIsolate(); 113 | 114 | // get a handle scope 115 | Scope scope(isolate); 116 | 117 | // the object that is being called 118 | auto obj = args.This(); 119 | 120 | // pointer to data 121 | auto *data = restore(isolate, obj); 122 | 123 | // if iterator was already destructed 124 | if (data == nullptr || data->done()) return retmethod(args); 125 | 126 | // create a new return object 127 | auto result = v8::Object::New(isolate); 128 | 129 | // set the value 130 | result->Set(scope, v8::String::NewFromUtf8Literal(isolate, "value"), data->current()).Check(); 131 | result->Set(scope, v8::String::NewFromUtf8Literal(isolate, "done"), v8::Boolean::New(isolate, !data->next())).Check(); 132 | 133 | // install the return-value 134 | args.GetReturnValue().Set(result); 135 | 136 | // if the iterator is done by now we can forget it 137 | if (data->done()) destruct(isolate, obj); 138 | } 139 | 140 | /** 141 | * Method that is called when the iterator leaps out prematurely 142 | * @param args 143 | */ 144 | void FromIterator::retmethod(const v8::FunctionCallbackInfo &args) 145 | { 146 | // the current isolate 147 | auto isolate = args.GetIsolate(); 148 | 149 | // get a handle scope 150 | Scope scope(isolate); 151 | 152 | // the object that is being called 153 | auto obj = args.This(); 154 | 155 | // destruct internally stored data 156 | destruct(isolate, obj); 157 | 158 | // create a new return object 159 | auto result = v8::Object::New(isolate); 160 | 161 | // set the "done" state 162 | result->Set(scope, v8::String::NewFromUtf8Literal(isolate, "done"), v8::True(isolate)).Check(); 163 | 164 | // install the return-value 165 | args.GetReturnValue().Set(result); 166 | } 167 | 168 | /** 169 | * End of namespace 170 | */ 171 | } 172 | 173 | -------------------------------------------------------------------------------- /fromiterator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * FromIterator.h 3 | * 4 | * Class that turns a PHP traversable object into something that 5 | * is iterable in a javascript environment too. 6 | * 7 | * @author Emiel Bruijntjes 8 | * @copyright 2025 Copernica BV 9 | */ 10 | 11 | /** 12 | * Include guard 13 | */ 14 | #pragma once 15 | 16 | /** 17 | * Dependencies 18 | */ 19 | #include "fromphp.h" 20 | 21 | /** 22 | * Begin of namespace 23 | */ 24 | namespace JS { 25 | 26 | /** 27 | * Class definition 28 | */ 29 | class FromIterator 30 | { 31 | private: 32 | /** 33 | * The variable that represents the iterator 34 | * @var v8::Local 35 | */ 36 | v8::Local _iterator; 37 | 38 | /** 39 | * Structure that holds the data associated with the iterator, 40 | * and that is added via a symbol in the javascript-object 41 | */ 42 | class Data 43 | { 44 | private: 45 | /** 46 | * Reference to the isolate 47 | * @var v8::Isolate 48 | */ 49 | v8::Isolate *_isolate; 50 | 51 | /** 52 | * The PHP space Iterator object 53 | * @var Php::Value 54 | */ 55 | Php::Value _value; 56 | 57 | public: 58 | /** 59 | * Constructor 60 | * @param isolate 61 | * @param iterator 62 | */ 63 | Data(v8::Isolate *isolate, const Php::Value &value) : 64 | _isolate(isolate), _value(value) {} 65 | 66 | /** 67 | * Destructor 68 | */ 69 | virtual ~Data() = default; 70 | 71 | /** 72 | * Is the iterator still valid? 73 | * @return bool 74 | */ 75 | bool valid() { return _value.call("valid"); } 76 | 77 | /** 78 | * Is the iterator done? 79 | * @return bool 80 | */ 81 | bool done() { return !valid(); } 82 | 83 | /** 84 | * Get the current value 85 | * @return Php::Value 86 | */ 87 | Php::Value value() { return _value.call("current"); } 88 | 89 | /** 90 | * Get the current value 91 | * @return v8::Local 92 | */ 93 | v8::Local current() { return FromPhp(_isolate, _value.call("current")); } 94 | 95 | /** 96 | * Proceed to the next record 97 | * @return bool 98 | */ 99 | bool next() { _value.call("next"); return valid(); } 100 | }; 101 | 102 | 103 | /** 104 | * Helper method to get access to the underlying data 105 | * @param isolate 106 | * @param obj 107 | * @return Data* 108 | */ 109 | static Data *restore(v8::Isolate *isolate, const v8::Local &obj); 110 | 111 | /** 112 | * Destruct internally stored data 113 | * @param isolate 114 | * @param obj 115 | * @return Data* 116 | */ 117 | static void destruct(v8::Isolate *isolate, const v8::Local &obj); 118 | 119 | /** 120 | * Method that is called by v8 when the next item is requested 121 | * @param args 122 | */ 123 | static void nxtmethod(const v8::FunctionCallbackInfo &args); 124 | 125 | /** 126 | * Method that is called when the iterator leaps out prematurely 127 | * @param args 128 | */ 129 | static void retmethod(const v8::FunctionCallbackInfo &args); 130 | 131 | 132 | public: 133 | /** 134 | * Constructor 135 | * @param isolate the isolate 136 | * @param value iterable php object 137 | */ 138 | FromIterator(v8::Isolate *isolate, const Php::Value &value); 139 | 140 | /** 141 | * Private destructor (object is self-destructing) 142 | */ 143 | virtual ~FromIterator() = default; 144 | 145 | /** 146 | * Cast to the local object 147 | * @return v8::Local 148 | */ 149 | v8::Local &value() { return _iterator; } 150 | }; 151 | 152 | /** 153 | * End of namespace 154 | */ 155 | } 156 | 157 | -------------------------------------------------------------------------------- /fromphp.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * FromPhp.cpp 3 | * 4 | * Implementation file for the FromPhp class 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2025 Copernica BV 8 | */ 9 | 10 | /** 11 | * Dependencies 12 | */ 13 | #include "fromphp.h" 14 | #include "core.h" 15 | 16 | /** 17 | * Begin of namespace 18 | */ 19 | namespace JS { 20 | 21 | /** 22 | * Constructor 23 | * @param isolate 24 | * @param value 25 | */ 26 | FromPhp::FromPhp(v8::Isolate *isolate, const Php::Value &value) 27 | { 28 | // check the type 29 | switch (value.type()) { 30 | case Php::Type::Null: operator=(v8::Null(isolate)); return; 31 | case Php::Type::Numeric: operator=(v8::Integer::New(isolate, value)); return; 32 | case Php::Type::Float: operator=(v8::Number::New(isolate, value)); return; 33 | case Php::Type::Bool: operator=(v8::Boolean::New(isolate, value)); return; 34 | case Php::Type::True: operator=(v8::Boolean::New(isolate, true)); return; 35 | case Php::Type::False: operator=(v8::Boolean::New(isolate, false)); return; 36 | case Php::Type::String: operator=(v8::String::NewFromUtf8(isolate, value).ToLocalChecked()); return; 37 | case Php::Type::Object: operator=(Core::upgrade(isolate)->wrap(value)); return; 38 | case Php::Type::Array: operator=(Core::upgrade(isolate)->wrap(value)); return; 39 | default: operator=(v8::Undefined(isolate)); return; 40 | } 41 | 42 | // @todo do we also need Callable? the old code did support this! 43 | } 44 | 45 | /** 46 | * End of namespace 47 | */ 48 | } 49 | 50 | -------------------------------------------------------------------------------- /fromphp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * FromPhp.h 3 | * 4 | * Class to convert a variable from php space into javascript context 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2025 Copernica BV 8 | */ 9 | 10 | /** 11 | * Include guard 12 | */ 13 | #pragma once 14 | 15 | /** 16 | * Dependencies 17 | */ 18 | #include 19 | #include 20 | 21 | /** 22 | * Begin of namespace 23 | */ 24 | namespace JS { 25 | 26 | /** 27 | * Class definition 28 | */ 29 | class FromPhp : public v8::Local 30 | { 31 | public: 32 | /** 33 | * Constructor 34 | * @param isolate 35 | * @param value 36 | */ 37 | FromPhp(v8::Isolate *isolate, const Php::Value &value); 38 | 39 | /** 40 | * Destructor 41 | */ 42 | virtual ~FromPhp() = default; 43 | 44 | /** 45 | * Expose base class assignment 46 | */ 47 | using v8::Local::operator=; 48 | }; 49 | 50 | /** 51 | * End of namespace 52 | */ 53 | } 54 | 55 | -------------------------------------------------------------------------------- /isolate.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * isolate.cpp 3 | * 4 | * Simple getter for the (unique) v8 isolate. This 5 | * method makes sure that a single isolate gets 6 | * constructed and that the v8 engine is properly 7 | * initialized once. 8 | * 9 | * Note that this is explicitly not thread-safe, 10 | * but it is fast. Since none of our other extensions 11 | * are properly thread-safe, this is an acceptable 12 | * limitation 13 | * 14 | * @copyright 2015 Copernica B.V. 15 | */ 16 | 17 | #if false 18 | 19 | /** 20 | * Dependencies 21 | */ 22 | #include "platform.h" 23 | #include "isolate.h" 24 | #include 25 | #include 26 | 27 | /** 28 | * Start namespace 29 | */ 30 | namespace JS { 31 | 32 | /** 33 | * The isolate instance for this thread 34 | * @var std::unique_ptr 35 | */ 36 | static thread_local std::unique_ptr isolate; 37 | 38 | class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { 39 | public: 40 | virtual void* Allocate(size_t length) { 41 | void* data = AllocateUninitialized(length); 42 | return data == NULL ? data : memset(data, 0, length); 43 | } 44 | virtual void* AllocateUninitialized(size_t length) 45 | { 46 | return malloc(length); 47 | } 48 | virtual void Free(void* data, size_t) 49 | { 50 | free(data); 51 | } 52 | }; 53 | 54 | static ArrayBufferAllocator allocator; 55 | 56 | /** 57 | * Constructor 58 | */ 59 | Isolate::Isolate() 60 | { 61 | // create a platform 62 | Platform::create(); 63 | 64 | // create our parameters 65 | v8::Isolate::CreateParams params; 66 | 67 | // set our custom allocator 68 | params.array_buffer_allocator = &allocator; 69 | 70 | // create the actual isolate 71 | _isolate = v8::Isolate::New(params); 72 | 73 | // associate ourselves with this isolate 74 | // so that we can find it back from the pointer 75 | _isolate->SetData(0, this); 76 | 77 | // and enter it 78 | _isolate->Enter(); 79 | } 80 | 81 | /** 82 | * Destructor 83 | */ 84 | Isolate::~Isolate() 85 | { 86 | // run all tasks still awaiting executing 87 | runTasks(); 88 | 89 | /** 90 | * the tasks that are still there are scheduled 91 | * somewhere in the future, we just delete these 92 | * - this seems like a strange thing to do, but 93 | * executing them now seems to throw v8 off guard 94 | * and results in either a deadlock or some weird 95 | * error message about garbage collection running 96 | * in some old "space" or something equally weird 97 | * and confusing. 98 | */ 99 | _tasks.clear(); 100 | 101 | // leave the isolate scope 102 | _isolate->Exit(); 103 | 104 | // clean it up 105 | _isolate->Dispose(); 106 | } 107 | 108 | /** 109 | * Perform all waiting tasks for this isolate 110 | */ 111 | void Isolate::runTasks() 112 | { 113 | // no tasks? then we're done fast! 114 | if (_tasks.empty()) return; 115 | 116 | // determine the current time 117 | auto now = std::chrono::system_clock::now(); 118 | 119 | // loop over all the tasks 120 | for (auto iter = _tasks.begin(); iter != _tasks.end(); ++iter) 121 | { 122 | // is the execution time still in the future? 123 | if (now < iter->first) 124 | { 125 | // first task? then don't remove anything 126 | if (iter == _tasks.begin()) return; 127 | 128 | // remove from the beginning up until the task 129 | // since we already moved past the last task we 130 | // need to go back one so we don't remove a task 131 | // we have not actually executed yet 132 | _tasks.erase(_tasks.begin(), --iter); 133 | 134 | // tasks executed and removed 135 | return; 136 | } 137 | 138 | // execute the task 139 | iter->second->Run(); 140 | } 141 | 142 | // we ran through all the tasks and executed all of them 143 | _tasks.clear(); 144 | } 145 | 146 | /** 147 | * Schedule a task to be executed 148 | * 149 | * @param isolate The isolate to execute the task under 150 | * @param task The task to execute 151 | * @param delay Number of seconds to wait before executing 152 | */ 153 | void Isolate::scheduleTask(v8::Isolate *isolate, v8::Task *task, double delay) 154 | { 155 | // first retrieve the isolate to schedule it under 156 | auto *real = static_cast(isolate->GetData(0)); 157 | 158 | // determine the time at which the task should be executed 159 | auto expire = std::chrono::system_clock::now() + std::chrono::microseconds{ static_cast(delay * 1000000) }; 160 | 161 | // schedule the task to be executed 162 | real->_tasks.emplace(std::make_pair(expire, std::unique_ptr{ task })); 163 | } 164 | 165 | /** 166 | * Get the isolate for this thread 167 | * 168 | * @return The thread-local isolate instance 169 | */ 170 | v8::Isolate *Isolate::get() 171 | { 172 | // do we still have to create the isolate? 173 | if (!isolate) isolate.reset(new Isolate); 174 | 175 | // execute tasks for this isolate 176 | isolate->runTasks(); 177 | 178 | // return the isolate 179 | return *isolate; 180 | } 181 | 182 | /** 183 | * Clean up the isolate - if any - for this thread 184 | */ 185 | void Isolate::destroy() 186 | { 187 | // remove the isolate 188 | isolate.reset(); 189 | } 190 | 191 | /** 192 | * Cast to the underlying isolate 193 | * 194 | * @return v8::Isolate* 195 | */ 196 | Isolate::operator v8::Isolate* () const 197 | { 198 | // return member 199 | return _isolate; 200 | } 201 | 202 | /** 203 | * End namespace 204 | */ 205 | } 206 | 207 | #endif 208 | -------------------------------------------------------------------------------- /isolate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * isolate.h 3 | * 4 | * Simple getter for the (unique) v8 isolate. This 5 | * method makes sure that a single isolate gets 6 | * constructed and that the v8 engine is properly 7 | * initialized once. 8 | * 9 | * Note that this is explicitly not thread-safe, 10 | * but it is fast. Since none of our other extensions 11 | * are properly thread-safe, this is an acceptable 12 | * limitation 13 | * 14 | * @copyright 2015 Copernica B.V. 15 | */ 16 | 17 | /** 18 | * Include guard 19 | */ 20 | #pragma once 21 | 22 | /** 23 | * Dependencies 24 | */ 25 | //#include 26 | //#include 27 | #include 28 | //#include 29 | 30 | /** 31 | * Start namespace 32 | */ 33 | namespace JS { 34 | 35 | /** 36 | * Forward declarations 37 | */ 38 | class Core; 39 | 40 | /** 41 | * Private class 42 | */ 43 | class Isolate final 44 | { 45 | private: 46 | /** 47 | * The create-params 48 | * v8::Isolate::CreateParams 49 | */ 50 | v8::Isolate::CreateParams _params; 51 | 52 | /** 53 | * The underlying isolate 54 | * @var v8::Isolate* 55 | */ 56 | v8::Isolate *_isolate; 57 | 58 | /** 59 | * Indexes for storing pointers 60 | * @var int 61 | */ 62 | static const int ISOLATE_INDEX = 0; 63 | static const int CORE_INDEX = 1; 64 | 65 | 66 | /** 67 | * List of tasks to execute 68 | * @var std::multimap> 69 | */ 70 | // std::multimap> _tasks; 71 | 72 | /** 73 | * Perform all waiting tasks for this isolate 74 | */ 75 | // void runTasks(); 76 | 77 | public: 78 | /** 79 | * Constructor 80 | * A core-pointer has to be passed to the isolate, to make the Core::upgrade() method work 81 | * @paran context 82 | */ 83 | Isolate(Core *core) 84 | { 85 | // we need an allocator 86 | _params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); 87 | 88 | // construct the isolate 89 | _isolate = v8::Isolate::New(_params); 90 | 91 | // store a pointer to the core 92 | _isolate->SetData(CORE_INDEX, core); 93 | 94 | // store a pointer to the isolate 95 | _isolate->SetData(ISOLATE_INDEX, this); 96 | } 97 | 98 | /** 99 | * No copying 100 | * @param that 101 | */ 102 | Isolate(const Isolate &that) = delete; 103 | 104 | /** 105 | * Destructor 106 | */ 107 | virtual ~Isolate() 108 | { 109 | // free up the isolate 110 | _isolate->Dispose(); 111 | 112 | // free up allocator 113 | delete _params.array_buffer_allocator; 114 | } 115 | 116 | /** 117 | * Get access to the isolate 118 | * @param isolate 119 | * @return Isolate 120 | */ 121 | static Isolate *update(v8::Isolate *isolate) 122 | { 123 | // is stored in a data field 124 | return static_cast(isolate->GetData(ISOLATE_INDEX)); 125 | } 126 | 127 | /** 128 | * Get access to the core 129 | * @param isolate 130 | * @return Core 131 | */ 132 | static Core *core(v8::Isolate *isolate) 133 | { 134 | // is stored in a data field 135 | return static_cast(isolate->GetData(CORE_INDEX)); 136 | } 137 | 138 | /** 139 | * Schedule a task to be executed 140 | * 141 | * @param isolate The isolate to schedule the task under 142 | * @param task The task to execute 143 | * @param delay Number of seconds to wait before executing 144 | */ 145 | //static void scheduleTask(v8::Isolate *isolate, v8::Task *task, double delay); 146 | 147 | /** 148 | * Get the isolate for this thread 149 | * 150 | * @return The thread-local isolate instance 151 | */ 152 | //static v8::Isolate *get(); 153 | 154 | /** 155 | * Clean up the isolate - if any - for this thread 156 | */ 157 | //static void destroy(); 158 | 159 | /** 160 | * Cast to the underlying isolate 161 | * @return v8::Isolate* 162 | */ 163 | operator v8::Isolate* () const 164 | { 165 | // expose underlying pointer 166 | return _isolate; 167 | } 168 | }; 169 | 170 | /** 171 | * End namespace 172 | */ 173 | } 174 | -------------------------------------------------------------------------------- /link.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Link.h 3 | * 4 | * Class that makes sure that an associated Php::Value object is 5 | * destructed when it falls out of scope 6 | * 7 | * @author Emiel Bruijntjes 8 | * @copyright 2025 Copernica BV 9 | */ 10 | 11 | /** 12 | * Include guard 13 | */ 14 | #pragma once 15 | 16 | /** 17 | * Begin of namespace 18 | */ 19 | namespace JS { 20 | 21 | /** 22 | * Class definition 23 | */ 24 | class Link 25 | { 26 | private: 27 | /** 28 | * The object to which the deleter is linked 29 | * This is a global object because it must stay in scope for as long as the object is not yet destructed, 30 | * it is turned into a weak-reference in the constructor (see below) so it will not keep the object in scope 31 | * @var v8::Global 32 | */ 33 | v8::Global _object; 34 | 35 | /** 36 | * The linked Php::Value (can also be a WeakReference instance) 37 | * @var Php::Value 38 | */ 39 | Php::Value _value; 40 | 41 | /** 42 | * Do we hold a weak reference to the PHP variable? 43 | * @var bool 44 | */ 45 | bool _weak = false; 46 | 47 | 48 | 49 | public: 50 | /** 51 | * Constructor to create a new link 52 | * @param isolate 53 | * @param object 54 | * @param value 55 | * @param weak 56 | */ 57 | Link(v8::Isolate *isolate, const v8::Local &object, const Php::Value &value, bool weak) : 58 | _object(isolate, object), 59 | _value(weak ? Php::call("WeakReference::create", value) : value), 60 | _weak(weak) 61 | { 62 | // install a function that will be called when the object is garbage collected 63 | _object.SetWeak(this, [](const v8::WeakCallbackInfo &info) { 64 | 65 | // get the original object 66 | Link *self = info.GetParameter(); 67 | 68 | // do the self-destruction 69 | delete self; 70 | 71 | }, v8::WeakCallbackType::kParameter); 72 | } 73 | 74 | /** 75 | * No copying 76 | * @param that 77 | */ 78 | Link(const Link &that) = delete; 79 | 80 | /** 81 | * Destructor 82 | */ 83 | virtual ~Link() 84 | { 85 | // remove the persistent object, this will 86 | _object.Reset(); 87 | } 88 | 89 | /** 90 | * Get the value 91 | * @return Php::Value 92 | */ 93 | Php::Value value() const 94 | { 95 | // non-weak values are stored "as is" 96 | if (!_weak) return _value; 97 | 98 | // the value must be an object (a WeakReference instance) 99 | if (!_value.isObject()) return nullptr; 100 | 101 | // get the underling object 102 | return _value.call("get"); 103 | } 104 | }; 105 | 106 | /** 107 | * End of namespace 108 | */ 109 | } 110 | -------------------------------------------------------------------------------- /linker.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Linker.cpp 3 | * 4 | * Implementation file for the Linker class 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2025 Copernica BV 8 | */ 9 | 10 | /** 11 | * Dependencies 12 | */ 13 | #include "linker.h" 14 | #include "link.h" 15 | #include "core.h" 16 | 17 | /** 18 | * Begin of namespace 19 | */ 20 | namespace JS { 21 | 22 | /** 23 | * Constructor 24 | * @param isolate the active isolate 25 | * @param key the private symbol that is associated storing external pointers 26 | * @param object the javascript object to be linked 27 | */ 28 | Linker::Linker(v8::Isolate *isolate, const v8::Global &key, const v8::Local &object) : 29 | _isolate(isolate), _key(key.Get(isolate)), _object(object) {} 30 | 31 | /** 32 | * Constructor 33 | * @param isolage the isolate 34 | * @param object the javascript object to be linked 35 | */ 36 | Linker::Linker(v8::Isolate *isolate, const v8::Local &object) : 37 | Linker(isolate, Core::upgrade(isolate)->symbol(), object) {} 38 | 39 | /** 40 | * Get the internal pointer 41 | * @return Link 42 | */ 43 | Link *Linker::pointer() const 44 | { 45 | // check if the private propery exists 46 | v8::MaybeLocal property = _object->GetPrivate(_isolate->GetCurrentContext(), _key); 47 | 48 | // if not set, or if not a pointer to an external thing 49 | if (property.IsEmpty()) return nullptr; 50 | 51 | // turn into a local 52 | v8::Local value = property.ToLocalChecked(); 53 | 54 | // should be external 55 | if (!value->IsExternal()) return nullptr; 56 | 57 | // get the external property, and cast to a Php::Value 58 | return static_cast(value.As()->Value()); 59 | } 60 | 61 | /** 62 | * Is the linker associated with a PHP object? 63 | * @return bool 64 | */ 65 | bool Linker::valid() const 66 | { 67 | // check the pointer 68 | return pointer() != nullptr; 69 | } 70 | 71 | /** 72 | * Associate the object with a PHP variable 73 | * @param value 74 | * @param weak 75 | * @return Php::Value 76 | */ 77 | const Php::Value &Linker::attach(const Php::Value &value, bool weak) 78 | { 79 | // remove the old pointer 80 | detach(); 81 | 82 | // make a brand new link 83 | auto *link = new Link(_isolate, _object, value, weak); 84 | 85 | // associate property 86 | _object->SetPrivate(_isolate->GetCurrentContext(), _key, v8::External::New(_isolate, link)); 87 | 88 | // done, expose the same value 89 | return value; 90 | } 91 | 92 | /** 93 | * Detach the PHP object from the javascript object 94 | */ 95 | void Linker::detach() 96 | { 97 | // get the link pointer 98 | Link *link = pointer(); 99 | 100 | // if not even set 101 | if (link == nullptr) return; 102 | 103 | // remove the link 104 | delete link; 105 | 106 | // remove private property 107 | _object->DeletePrivate(_isolate->GetCurrentContext(), _key); 108 | } 109 | 110 | /** 111 | * Expose the object in PHP space 112 | * @return Php::Value 113 | */ 114 | Php::Value Linker::value() const 115 | { 116 | // get the link pointer 117 | Link *link = pointer(); 118 | 119 | // if not set 120 | if (link == nullptr) return nullptr; 121 | 122 | // get the php value 123 | return link->value(); 124 | } 125 | 126 | /** 127 | * End of namespace 128 | */ 129 | } 130 | 131 | -------------------------------------------------------------------------------- /linker.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Linker.h 3 | * 4 | * Class that can be used to link a javascript-object with a PHP-object, 5 | * so that if the javascript is returned multiple times to PHP space, 6 | * that we always return the same PHP object. 7 | * 8 | * It also makes sure that once the javascript object is destructed, 9 | * the associated PHP object is destructed too. 10 | * 11 | * @author Emiel Bruijntjes 12 | * @copyright 2025 Copernica BV 13 | */ 14 | 15 | /** 16 | * Include guard 17 | */ 18 | #pragma once 19 | 20 | /** 21 | * Dependencies 22 | */ 23 | #include 24 | #include 25 | 26 | /** 27 | * Begin of namespace 28 | */ 29 | namespace JS { 30 | 31 | /** 32 | * Forward declarations 33 | */ 34 | class Link; 35 | 36 | /** 37 | * Class definition 38 | */ 39 | class Linker 40 | { 41 | private: 42 | /** 43 | * The active isolate 44 | * @var v8::Isolate 45 | */ 46 | v8::Isolate *_isolate; 47 | 48 | /** 49 | * The private symbol for lookups of the external pointer 50 | * @var v8::Local 51 | */ 52 | v8::Local _key; 53 | 54 | /** 55 | * The underlying object 56 | * @var v8::Local 57 | */ 58 | v8::Local _object; 59 | 60 | /** 61 | * Helper function to get access to the pointer to the Php::Value 62 | * @return Link 63 | */ 64 | Link *pointer() const; 65 | 66 | /** 67 | * Constructor 68 | * @param isolate the active isolate 69 | * @param key the private symbol that is associated storing external pointers 70 | * @param object the javascript object to be linked 71 | */ 72 | Linker(v8::Isolate *isolate, const v8::Global &key, const v8::Local &object); 73 | 74 | public: 75 | /** 76 | * Constructor 77 | * @param isolate the active isolate 78 | * @param object the javascript object to be linked 79 | */ 80 | Linker(v8::Isolate *isolate, const v8::Local &object); 81 | 82 | /** 83 | * No copying 84 | * @param that 85 | */ 86 | Linker(const Linker &that) = delete; 87 | 88 | /** 89 | * Destructor 90 | */ 91 | virtual ~Linker() = default; 92 | 93 | /** 94 | * Is the linker associated with a PHP object? 95 | * @return bool 96 | */ 97 | bool valid() const; 98 | 99 | /** 100 | * Associate the object with a PHP variable 101 | * @param value 102 | * @param weak 103 | * @return Php::Value 104 | */ 105 | const Php::Value &attach(const Php::Value &value, bool weak = false); 106 | 107 | /** 108 | * Detach the PHP object from the javascript object 109 | */ 110 | void detach(); 111 | 112 | /** 113 | * Expose the object in PHP space 114 | * @return Php::Value 115 | */ 116 | Php::Value value() const; 117 | }; 118 | 119 | /** 120 | * End of namespace 121 | */ 122 | } 123 | 124 | -------------------------------------------------------------------------------- /names.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Names.h 3 | * 4 | * One central place in which we define the classnames 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2025 Copernica BV 8 | */ 9 | 10 | /** 11 | * Include guard 12 | */ 13 | #pragma once 14 | 15 | /** 16 | * Begin of namespace 17 | */ 18 | namespace JS { 19 | 20 | /** 21 | * All classnames 22 | */ 23 | struct Names { 24 | // class names 25 | inline static const char *Script = "JS\\Script"; 26 | inline static const char *Object = "JS\\Object"; 27 | inline static const char *Context = "JS\\Context"; 28 | inline static const char *Function = "JS\\Function"; 29 | 30 | // constants 31 | inline static const char *None = "JS\\None"; 32 | inline static const char *ReadOnly = "JS\\ReadOnly"; 33 | inline static const char *DontDelete = "JS\\DontDelete"; 34 | inline static const char *DontEnumerate = "JS\\DontEnumerate"; 35 | 36 | 37 | }; 38 | 39 | /** 40 | * End of namespace 41 | */ 42 | } 43 | 44 | -------------------------------------------------------------------------------- /php-js.ini: -------------------------------------------------------------------------------- 1 | ; enable the extension 2 | extension = php-js.so 3 | -------------------------------------------------------------------------------- /php_array.h: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpArray.h 3 | * 4 | * Helper class to turn a v8/javascript array into a php array 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2025 Copernica BV 8 | */ 9 | 10 | /** 11 | * Include guard 12 | */ 13 | #pragma once 14 | 15 | /** 16 | * Dependencies 17 | */ 18 | #include "core.h" 19 | #include "php_variable.h" 20 | 21 | /** 22 | * Begin of namespace 23 | */ 24 | namespace JS { 25 | 26 | /** 27 | * Class definition 28 | */ 29 | class PhpArray : public Php::Array 30 | { 31 | public: 32 | /** 33 | * Contructor based on a v8::Array 34 | * @param isolate 35 | * @param input 36 | */ 37 | PhpArray(v8::Isolate *isolate, const v8::Local &input) 38 | { 39 | // we need a context 40 | auto ctx = isolate->GetCurrentContext(); 41 | 42 | // iterate over the input array 43 | for (uint32_t i = 0; i < input->Length(); ++i) 44 | { 45 | // get item from the array 46 | v8::MaybeLocal maybe = input->Get(ctx, i); 47 | if (maybe.IsEmpty()) continue; 48 | 49 | // the underlying element (arrays can be sparse) 50 | v8::Local element = maybe.ToLocalChecked(); 51 | if (element->IsUndefined()) continue; 52 | 53 | // set this in the output array 54 | set(i, PhpVariable(isolate, element)); 55 | } 56 | } 57 | 58 | /** 59 | * Contructor based on a v8::Array 60 | * @param isolate 61 | * @param input 62 | */ 63 | PhpArray(v8::Isolate *isolate, const v8::FunctionCallbackInfo &input) 64 | { 65 | // iterate over the input array 66 | for (int i = 0; i < input.Length(); ++i) 67 | { 68 | // set this in the output array 69 | set(i, PhpVariable(isolate, input[i])); 70 | } 71 | } 72 | 73 | /** 74 | * Constructor for arguments 75 | * @param info 76 | */ 77 | PhpArray(const v8::FunctionCallbackInfo &args) : 78 | PhpArray(args.GetIsolate(), args) {} 79 | 80 | /** 81 | * Destructor 82 | */ 83 | virtual ~PhpArray() = default; 84 | }; 85 | 86 | /** 87 | * End of namespace 88 | */ 89 | } 90 | -------------------------------------------------------------------------------- /php_base.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpBase.cpp 3 | * 4 | * Implementation file for the PhpBase class 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2015 - 2025 Copernica B.V. 8 | */ 9 | 10 | /** 11 | * Dependencies 12 | */ 13 | #include "php_base.h" 14 | #include "names.h" 15 | #include "core.h" 16 | 17 | /** 18 | * Start namespace 19 | */ 20 | namespace JS { 21 | 22 | /** 23 | * Constructor 24 | * @param isolate The isolate 25 | * @param object The ecmascript object 26 | */ 27 | PhpBase::PhpBase(v8::Isolate *isolate, const v8::Local &object) : 28 | _core(Core::upgrade(isolate)), 29 | _object(isolate, object) {} 30 | 31 | /** 32 | * Destructor 33 | */ 34 | PhpBase::~PhpBase() 35 | { 36 | // forget the associated javascript object 37 | _object.Reset(); 38 | } 39 | 40 | /** 41 | * Helper method to unwrap an object 42 | * @param core 43 | * @param value 44 | * @return Php::Object 45 | */ 46 | PhpBase *PhpBase::unwrap(const Core *core, const Php::Value &value) 47 | { 48 | // must be the right class 49 | if (!value.instanceOf(Names::Object) && !value.instanceOf(Names::Function)) return nullptr; 50 | 51 | // get self-pointe 52 | PhpBase *self = (PhpBase *)value.implementation(); 53 | 54 | // if not set (should not occur) 55 | if (self == nullptr) return nullptr; 56 | 57 | // check if the object comes from the same core! 58 | return self->_core.get() == core ? self : nullptr; 59 | } 60 | 61 | /** 62 | * Get the v8 handle 63 | * @return v8::Local 64 | */ 65 | v8::Local PhpBase::handle() 66 | { 67 | // get the local handle back 68 | return _object.Get(_core->isolate()); 69 | } 70 | 71 | /** 72 | * End namespace 73 | */ 74 | } 75 | 76 | -------------------------------------------------------------------------------- /php_base.h: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpBase.h 3 | * 4 | * Base class for objects inside v8 that are exposed to PHP space. 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2015 - 2025 Copernica B.V. 8 | */ 9 | 10 | /** 11 | * Include guard 12 | */ 13 | #pragma once 14 | 15 | /** 16 | * Dependencies 17 | */ 18 | #include 19 | #include 20 | 21 | /** 22 | * Start namespace 23 | */ 24 | namespace JS { 25 | 26 | /** 27 | * Forward declarations 28 | */ 29 | class Core; 30 | 31 | /** 32 | * Class definition 33 | */ 34 | class PhpBase : public Php::Base 35 | { 36 | protected: 37 | /** 38 | * The core in which we operate (this is a shared pointer because for as long as the 39 | * object lives in PHP space, we want to keep the core around (even when the PHP 40 | * space JS\Context already fell out of scope) 41 | * @var std::shared_ptr 42 | */ 43 | std::shared_ptr _core; 44 | 45 | /** 46 | * The underlying ecmascript object 47 | * @var v8::Global 48 | */ 49 | v8::Global _object; 50 | 51 | 52 | /** 53 | * Constructor 54 | * @param isolate The isolate 55 | * @param object The ecmascript object 56 | */ 57 | PhpBase(v8::Isolate *isolate, const v8::Local &object); 58 | 59 | public: 60 | /** 61 | * No copying 62 | * @param that 63 | */ 64 | PhpBase(const PhpBase &that) = delete; 65 | 66 | /** 67 | * Destructor 68 | */ 69 | virtual ~PhpBase(); 70 | 71 | /** 72 | * Helper method to unwrap an object 73 | * @param core 74 | * @param value 75 | * @return Php::Base 76 | */ 77 | static PhpBase *unwrap(const Core *core, const Php::Value &value); 78 | 79 | /** 80 | * Get the v8 handle 81 | * @return v8::Local 82 | */ 83 | v8::Local handle(); 84 | }; 85 | 86 | /** 87 | * End namespace 88 | */ 89 | } 90 | 91 | -------------------------------------------------------------------------------- /php_context.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpContext.cpp 3 | * 4 | * Implementation file for the PhpContext class 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2025 Copernica BV 8 | */ 9 | 10 | /** 11 | * Dependencies 12 | */ 13 | #include "php_context.h" 14 | 15 | /** 16 | * Begin of namespace 17 | */ 18 | namespace JS { 19 | 20 | /** 21 | * Constructor 22 | */ 23 | PhpContext::PhpContext() : _core(std::make_shared()) {} 24 | 25 | /** 26 | * Assign a variable to the javascript context 27 | * @param params array of parameters: 28 | * - string name of property to assign required 29 | * - mixed property value to assign required 30 | * - integer property attributes optional 31 | * 32 | * The property attributes can be one of the following values 33 | * 34 | * - ReadOnly 35 | * - DontEnum 36 | * - DontDelete 37 | * 38 | * If not specified, the property will be writable, enumerable and 39 | * deletable. 40 | */ 41 | Php::Value PhpContext::assign(Php::Parameters ¶ms) 42 | { 43 | // pass on 44 | return _core->assign(params[0], params[1], params.size() > 2 ? params[2] : Php::Value(v8::None)); 45 | } 46 | 47 | /** 48 | * Parse a piece of javascript code 49 | * 50 | * @param params array with one parameter: the code to execute 51 | * @return Php::Value 52 | * @throws Php::Exception 53 | */ 54 | Php::Value PhpContext::evaluate(Php::Parameters ¶ms) 55 | { 56 | // pass on 57 | return _core->evaluate(params[0], params.size() > 1 ? params[1] : Php::Value(0)); 58 | } 59 | 60 | /** 61 | * End of namespace 62 | */ 63 | } 64 | 65 | -------------------------------------------------------------------------------- /php_context.h: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpContext.h 3 | * 4 | * The main javascript context class as it is exposed to PHP space 5 | * 6 | * @copyright 2015 - 2025 Copernica B.V. 7 | */ 8 | 9 | /** 10 | * Include guard 11 | */ 12 | #pragma once 13 | 14 | /** 15 | * Dependencies 16 | */ 17 | #include 18 | #include "core.h" 19 | 20 | /** 21 | * Start namespace 22 | */ 23 | namespace JS { 24 | 25 | /** 26 | * Class definition 27 | */ 28 | class PhpContext : public Php::Base 29 | { 30 | private: 31 | /** 32 | * Shared pointer to the actual core data 33 | * @var std::shared_ptr 34 | */ 35 | std::shared_ptr _core; 36 | 37 | public: 38 | /** 39 | * Constructor 40 | */ 41 | PhpContext(); 42 | 43 | /** 44 | * No copying allowed 45 | * @param that the object we cannot copy 46 | */ 47 | PhpContext(const PhpContext &that) = delete; 48 | 49 | /** 50 | * Destructor 51 | */ 52 | virtual ~PhpContext() = default; 53 | 54 | /** 55 | * Assign a variable to the javascript context 56 | * 57 | * @param params array of parameters: 58 | * - string name of property to assign required 59 | * - mixed property value to assign required 60 | * - integer property attributes optional 61 | * 62 | * The property attributes can be one of the following values 63 | * 64 | * - ReadOnly 65 | * - DontEnum 66 | * - DontDelete 67 | * 68 | * If not specified, the property will be writable, enumerable and 69 | * deletable. 70 | * 71 | * @return Php::Value 72 | */ 73 | Php::Value assign(Php::Parameters ¶ms); 74 | 75 | /** 76 | * Parse a piece of javascript code 77 | * @param params array with one parameter: the code to execute 78 | * @return Php::Value 79 | * @throws Php::Exception 80 | */ 81 | Php::Value evaluate(Php::Parameters ¶ms); 82 | }; 83 | 84 | /** 85 | * End namespace 86 | */ 87 | } 88 | -------------------------------------------------------------------------------- /php_exception.h: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpException.h 3 | * 4 | * Helper class to convert an exception from javascript-space into an 5 | * exception for php-space 6 | * 7 | * @author Emiel Bruijntjes 8 | * @copyright 2025 Copernica BV 9 | */ 10 | 11 | /** 12 | * Include guard 13 | */ 14 | #pragma once 15 | 16 | /** 17 | * Begin of namespace 18 | */ 19 | namespace JS { 20 | 21 | /** 22 | * Class definition 23 | */ 24 | class PhpException : public Php::Exception 25 | { 26 | private: 27 | /** 28 | * Constructor 29 | * @param message 30 | */ 31 | PhpException(const v8::String::Utf8Value &message) : 32 | Php::Exception(std::string(*message, message.length())) {} 33 | 34 | /** 35 | * Constructor 36 | * @param isolate 37 | * @param message 38 | */ 39 | PhpException(v8::Isolate *isolate, const v8::Local &message) : 40 | PhpException(v8::String::Utf8Value(isolate, message)) {} 41 | 42 | /** 43 | * Constructor 44 | * @param isolate 45 | * @param message 46 | */ 47 | PhpException(v8::Isolate *isolate, const v8::Local &message) : 48 | PhpException(isolate, message->Get()) {} 49 | 50 | public: 51 | /** 52 | * Constructor 53 | * @param isolate 54 | * @param catcher 55 | */ 56 | PhpException(v8::Isolate *isolate, const v8::TryCatch &catcher) : 57 | PhpException(isolate, catcher.Message()) {} 58 | 59 | /** 60 | * Destructor 61 | */ 62 | virtual ~PhpException() = default; 63 | }; 64 | 65 | /** 66 | * End of namespace 67 | */ 68 | } 69 | 70 | -------------------------------------------------------------------------------- /php_function.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpFunctiom.cpp 3 | * 4 | * Implementation file for the PhpFunction class 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2025 Copernica BV 8 | */ 9 | 10 | /** 11 | * Dependencies 12 | */ 13 | #include "php_function.h" 14 | #include "php_variable.h" 15 | #include "php_exception.h" 16 | #include "fromphp.h" 17 | #include "scope.h" 18 | 19 | /** 20 | * Start namespace 21 | */ 22 | namespace JS { 23 | 24 | /** 25 | * Method that is called when the function is invoked 26 | * @param params 27 | * @return Php::Value 28 | */ 29 | Php::Value PhpFunction::__invoke(Php::Parameters ¶ms) 30 | { 31 | // scope for the call 32 | Scope scope(_core); 33 | 34 | // get the function in a local variable 35 | v8::Local func(_object.Get(_core->isolate()).As()); 36 | 37 | // catch any errors that occur while either compiling or running the script 38 | v8::TryCatch catcher(_core->isolate()); 39 | 40 | // create a new array with parameters 41 | std::vector> array; 42 | array.reserve(params.size()); 43 | 44 | // iterate over all the given parameters and add them to the arrau 45 | for (auto ¶m: params) array.push_back(FromPhp(_core->isolate(), param)); 46 | 47 | // now we can actually call the function 48 | auto result = func->Call(_core->isolate(), scope, v8::Undefined(_core->isolate()), array.size(), array.data()); 49 | 50 | // did we catch an exception? 51 | if (!catcher.HasCaught()) return result.IsEmpty() ? Php::Value(nullptr) : PhpVariable(_core->isolate(), result.ToLocalChecked()); 52 | 53 | // throw the exception 54 | throw PhpException(_core->isolate(), catcher); 55 | } 56 | 57 | /** 58 | * End of namespace 59 | */ 60 | } 61 | -------------------------------------------------------------------------------- /php_function.h: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpFunction.h 3 | * 4 | * Class that wraps around an ecmascript function and makes it available to PHP 5 | * userspace. 6 | * 7 | * @author Emiel Bruijntjes 8 | * @copyright 2025 Copernica B.V. 9 | */ 10 | 11 | /** 12 | * Include guard 13 | */ 14 | #pragma once 15 | 16 | /** 17 | * Dependencies 18 | */ 19 | #include "php_base.h" 20 | 21 | /** 22 | * Start namespace 23 | */ 24 | namespace JS { 25 | 26 | /** 27 | * Class definition 28 | */ 29 | class PhpFunction : public PhpBase 30 | { 31 | public: 32 | /** 33 | * Constructor 34 | * @param isolate The isolate 35 | * @param object The ecmascript object 36 | */ 37 | PhpFunction(v8::Isolate *isolate, const v8::Local &object) : 38 | PhpBase(isolate, object) {} 39 | 40 | /** 41 | * No copying 42 | * @param that 43 | */ 44 | PhpFunction(const PhpFunction &that) = delete; 45 | 46 | /** 47 | * Destructor 48 | */ 49 | virtual ~PhpFunction() = default; 50 | 51 | /** 52 | * Method that is called when the function is invoked 53 | * @param params 54 | * @return Php::Value 55 | */ 56 | Php::Value __invoke(Php::Parameters ¶ms); 57 | }; 58 | 59 | /** 60 | * End namespace 61 | */ 62 | } 63 | 64 | -------------------------------------------------------------------------------- /php_iterator.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpIterator.cpp 3 | * 4 | * Implementation file for the PhpIterator class 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2025 Copernica BV 8 | */ 9 | 10 | /** 11 | * Dependencies 12 | */ 13 | #include "php_iterator.h" 14 | #include "scope.h" 15 | #include "php_variable.h" 16 | 17 | /** 18 | * Begin of namespace 19 | */ 20 | namespace JS { 21 | 22 | /** 23 | * Constructor 24 | * @param base The base that PHP-CPP insists on 25 | * @param core The javascript core 26 | * @param object The object to iterate 27 | */ 28 | PhpIterator::PhpIterator(Php::Base *base, const std::shared_ptr &core, const v8::Local &object) : Php::Iterator(base), 29 | _core(core), 30 | _object(core->isolate(), object) 31 | { 32 | // get a scope (we already have one when we are called, but ok) 33 | Scope scope(core); 34 | 35 | // get the key (this is a maybe) 36 | auto maybe = object->GetPropertyNames(scope); 37 | if (maybe.IsEmpty()) return; 38 | 39 | // convert to a v8::Local 40 | auto keys = maybe.ToLocalChecked(); 41 | 42 | // store in _keys, which is a v8::Global 43 | _keys.Reset(core->isolate(), keys); 44 | 45 | // size can be helpful to have in a cached form 46 | _size = keys->Length(); 47 | } 48 | 49 | /** 50 | * Destructor 51 | */ 52 | PhpIterator::~PhpIterator() 53 | { 54 | // destruct handles 55 | _object.Reset(); 56 | _keys.Reset(); 57 | } 58 | 59 | /** 60 | * Is the iterator still valid? 61 | * @return is an element present at the current offset 62 | */ 63 | bool PhpIterator::valid() 64 | { 65 | // we should not be out of bounds 66 | return _position < _size; 67 | } 68 | 69 | /** 70 | * Retrieve the current value 71 | * @return value at current offset 72 | */ 73 | Php::Value PhpIterator::current() 74 | { 75 | // make sure there is a scope 76 | Scope scope(_core); 77 | 78 | // get the object and keys in a local variables 79 | v8::Local object(_object.Get(_core->isolate())); 80 | v8::Local keys(_keys.Get(_core->isolate())); 81 | 82 | // retrieve the current key 83 | auto key = keys->Get(scope, _position); 84 | if (key.IsEmpty()) return nullptr; 85 | 86 | // retrieve the current key, the value and convert it 87 | auto value = object->Get(scope, key.ToLocalChecked()); 88 | if (key.IsEmpty()) return nullptr; 89 | 90 | // expose to php space 91 | return PhpVariable(_core->isolate(), value.ToLocalChecked()); 92 | } 93 | 94 | /** 95 | * Retrieve the current key 96 | * @return the current key 97 | */ 98 | Php::Value PhpIterator::key() 99 | { 100 | // make sure there is a scope 101 | Scope scope(_core); 102 | 103 | // get the keys in a local variable 104 | v8::Local keys(_keys.Get(_core->isolate())); 105 | 106 | // retrieve the current key 107 | auto key = keys->Get(scope, _position); 108 | if (key.IsEmpty()) return nullptr; 109 | 110 | // retrieve the current key, the value and convert it 111 | return PhpVariable(_core->isolate(), key.ToLocalChecked()); 112 | } 113 | 114 | /** 115 | * Move ahead to the next item 116 | */ 117 | void PhpIterator::next() 118 | { 119 | // move to the next position 120 | ++_position; 121 | } 122 | 123 | /** 124 | * Start over at the beginning 125 | */ 126 | void PhpIterator::rewind() 127 | { 128 | // move back to the beginning 129 | _position = 0; 130 | } 131 | 132 | /** 133 | * End of namespace 134 | */ 135 | } 136 | 137 | -------------------------------------------------------------------------------- /php_iterator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpIterator.h 3 | * 4 | * Class to iterate over a JS\Object. This instance is constructed 5 | * by the JSObject::getIterator() method. 6 | * 7 | * @author Emiel Bruijntjes 8 | * @copyright 2025 Copernica BV 9 | */ 10 | 11 | /** 12 | * Include guard 13 | */ 14 | #pragma once 15 | 16 | /** 17 | * Dependencies 18 | */ 19 | #include 20 | #include 21 | #include 22 | 23 | /** 24 | * Begin of namespace 25 | */ 26 | namespace JS { 27 | 28 | /** 29 | * Class definition 30 | */ 31 | class PhpIterator : public Php::Iterator 32 | { 33 | private: 34 | /** 35 | * The javascript core 36 | * @var std::shared_ptr 37 | */ 38 | std::shared_ptr _core; 39 | 40 | /** 41 | * The underlying ecmascript object 42 | * @var Stack 43 | */ 44 | v8::Global _object; 45 | 46 | /** 47 | * All properties in the object 48 | * @var Stack 49 | */ 50 | v8::Global _keys; 51 | 52 | /** 53 | * Current position in the object 54 | * @var uint32_t 55 | */ 56 | uint32_t _position = 0; 57 | 58 | /** 59 | * Number of valid keys 60 | * @var uint32_t 61 | */ 62 | uint32_t _size = 0; 63 | 64 | public: 65 | /** 66 | * Constructor 67 | * @param base The base that PHP-CPP insists on 68 | * @param core The javascript core 69 | * @param object The object to iterate 70 | */ 71 | PhpIterator(Php::Base *base, const std::shared_ptr &core, const v8::Local &object); 72 | 73 | /** 74 | * Destructor 75 | */ 76 | virtual ~PhpIterator(); 77 | 78 | /** 79 | * Is the iterator still valid? 80 | * @return is an element present at the current offset 81 | */ 82 | virtual bool valid() override; 83 | 84 | /** 85 | * Retrieve the current value 86 | * @return value at current offset 87 | */ 88 | virtual Php::Value current() override; 89 | 90 | /** 91 | * Retrieve the current key 92 | * @return the current key 93 | */ 94 | virtual Php::Value key() override; 95 | 96 | /** 97 | * Move ahead to the next item 98 | */ 99 | virtual void next() override; 100 | 101 | /** 102 | * Start over at the beginning 103 | */ 104 | virtual void rewind() override; 105 | }; 106 | 107 | /** 108 | * End of namespace 109 | */ 110 | } 111 | 112 | -------------------------------------------------------------------------------- /php_object.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpObject.cpp 3 | * 4 | * Class that wraps around an ecmascript object and makes it available to PHP userspace. 5 | * 6 | * @author Emiel Bruijntjes 7 | * @copyright 2015 - 2025 Copernica B.V. 8 | */ 9 | 10 | /** 11 | * Dependencies 12 | */ 13 | #include "php_object.h" 14 | #include "scope.h" 15 | #include "fromphp.h" 16 | #include "php_variable.h" 17 | #include "php_iterator.h" 18 | #include "php_exception.h" 19 | #include "names.h" 20 | 21 | /** 22 | * Start namespace 23 | */ 24 | namespace JS { 25 | 26 | /** 27 | * Retrieve a property 28 | * @param name Name of the property 29 | * @return The requested property 30 | */ 31 | Php::Value PhpObject::__get(const Php::Value &name) const 32 | { 33 | // scope for the call 34 | Scope scope(_core); 35 | 36 | // get the object in a local variable 37 | v8::Local object(_object.Get(_core->isolate()).As()); 38 | 39 | // get the property value 40 | auto property = object->Get(scope, FromPhp(_core->isolate(), name)); 41 | 42 | // if it does not exist, we fall back on the default behavior 43 | if (property.IsEmpty()) return Php::Base::__get(name); 44 | 45 | // convert the value to a PHP value 46 | return PhpVariable(_core->isolate(), property.ToLocalChecked()); 47 | } 48 | 49 | /** 50 | * Change a property 51 | * @param name Name of the property 52 | * @param property New value for the property 53 | */ 54 | void PhpObject::__set(const Php::Value &name, const Php::Value &property) 55 | { 56 | // scope for the call 57 | Scope scope(_core); 58 | 59 | // get the object in a local variable 60 | v8::Local object(_object.Get(_core->isolate()).As()); 61 | 62 | // convert the value to a ecmascript value and store it (we explicitly want to ignore the return-value) 63 | object->Set(scope, FromPhp(_core->isolate(), name), FromPhp(_core->isolate(), property)).Check(); 64 | } 65 | 66 | /** 67 | * Check if a property is set 68 | * @param name Name of the property 69 | * @return Is the property set 70 | */ 71 | bool PhpObject::__isset(const Php::Value &name) 72 | { 73 | // scope for the call 74 | Scope scope(_core); 75 | 76 | // get the object in a local variable 77 | v8::Local object(_object.Get(_core->isolate()).As()); 78 | 79 | // check if the object has the requested property 80 | auto result = object->Has(scope, FromPhp(_core->isolate(), name)); 81 | 82 | // check for success 83 | return result.IsJust() && result.FromJust(); 84 | } 85 | 86 | /** 87 | * Call a function 88 | * @param name Name of the function to call 89 | * @param params The input parameters 90 | * @return The result of the function call 91 | */ 92 | Php::Value PhpObject::__call(const char *name, Php::Parameters ¶ms) 93 | { 94 | // scope for the call 95 | Scope scope(_core); 96 | 97 | // get the object in a local variable 98 | v8::Local object(_object.Get(_core->isolate()).As()); 99 | 100 | // construct the method name 101 | auto methodname = v8::String::NewFromUtf8(_core->isolate(), name); 102 | if (methodname.IsEmpty()) throw Php::Exception("invalid method name"); 103 | 104 | // variables to store property 105 | v8::Local property; 106 | 107 | // do the checks 108 | if (!object->Get(scope, methodname.ToLocalChecked()).ToLocal(&property)) throw Php::Exception("no such property"); 109 | 110 | // it must be a function 111 | if (!property->IsFunction()) throw Php::Exception("not a method"); 112 | 113 | // convert to a function 114 | v8::Local method = property.As(); 115 | 116 | // we need the parameters 117 | std::vector> args; 118 | 119 | // convert the parameters 120 | for (size_t i = 0; i < params.size(); ++i) 121 | { 122 | // set a parameter 123 | args.push_back(FromPhp(_core->isolate(), params[i])); 124 | } 125 | 126 | // catch any errors that occur while either compiling or running the script 127 | v8::TryCatch catcher(_core->isolate()); 128 | 129 | // the result 130 | auto result = method->Call(scope, object, args.size(), args.data()); 131 | 132 | // no exception 133 | if (!catcher.HasCaught()) return result.IsEmpty() ? Php::Value(nullptr) : PhpVariable(_core->isolate(), result.ToLocalChecked()); 134 | 135 | // pass this exception on to PHP userspace 136 | throw PhpException(_core->isolate(), catcher); 137 | } 138 | 139 | /** 140 | * Cast to a string 141 | * @return The result of the string conversion 142 | */ 143 | Php::Value PhpObject::__toString() 144 | { 145 | // scope for the call 146 | Scope scope(_core); 147 | 148 | // get the object in a local variable 149 | v8::Local object(_object.Get(_core->isolate()).As()); 150 | 151 | // convert to string and then to php 152 | auto result = object->ToString(scope); 153 | 154 | // if not set 155 | if (result.IsEmpty()) return nullptr; 156 | 157 | // convert to php space 158 | return PhpVariable(_core->isolate(), result.ToLocalChecked()); 159 | } 160 | 161 | /** 162 | * Retrieve the iterator 163 | * @return The iterator 164 | */ 165 | Php::Iterator *PhpObject::getIterator() 166 | { 167 | // scope for the call 168 | Scope scope(_core); 169 | 170 | // get the object in a local variable 171 | v8::Local object(_object.Get(_core->isolate()).As()); 172 | 173 | // create a new iterator instance, cleaned up by PHP-CPP 174 | return new PhpIterator(this, _core, object); 175 | } 176 | 177 | /** 178 | * End namespace 179 | */ 180 | } 181 | 182 | -------------------------------------------------------------------------------- /php_object.h: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpObject.h 3 | * 4 | * Class that wraps around an ecmascript object and makes it available to PHP 5 | * userspace. 6 | * 7 | * @author Emiel Bruijntjes 8 | * @copyright 2015 - 2025 Copernica B.V. 9 | */ 10 | 11 | /** 12 | * Include guard 13 | */ 14 | #pragma once 15 | 16 | /** 17 | * Dependencies 18 | */ 19 | #include "php_base.h" 20 | 21 | /** 22 | * Start namespace 23 | */ 24 | namespace JS { 25 | 26 | /** 27 | * Class definition 28 | */ 29 | class PhpObject : public PhpBase, public Php::Traversable 30 | { 31 | public: 32 | /** 33 | * Constructor 34 | * @param isolate The isolate 35 | * @param object The ecmascript object 36 | */ 37 | PhpObject(v8::Isolate *isolate, const v8::Local &object) : 38 | PhpBase(isolate, object) {} 39 | 40 | /** 41 | * No copying 42 | * @param that 43 | */ 44 | PhpObject(const PhpObject &that) = delete; 45 | 46 | /** 47 | * Destructor 48 | */ 49 | virtual ~PhpObject() = default; 50 | 51 | /** 52 | * Retrieve a property 53 | * @param name Name of the property 54 | * @return The requested property 55 | */ 56 | Php::Value __get(const Php::Value &name) const; 57 | 58 | /** 59 | * Change a property 60 | * @param name Name of the property 61 | * @param property New value for the property 62 | */ 63 | void __set(const Php::Value &name, const Php::Value &property); 64 | 65 | /** 66 | * Check if a property is set 67 | * @param name Name of the property 68 | * @return Is the property set 69 | */ 70 | bool __isset(const Php::Value &name); 71 | 72 | /** 73 | * Call a function 74 | * @param name Name of the function to call 75 | * @param params The input parameters 76 | * @return The result of the function call 77 | */ 78 | Php::Value __call(const char *name, Php::Parameters ¶ms); 79 | 80 | /** 81 | * Cast to a string 82 | * @return The result of the string conversion 83 | */ 84 | Php::Value __toString(); 85 | 86 | /** 87 | * Retrieve the iterator 88 | * @return The iterator 89 | */ 90 | virtual Php::Iterator *getIterator() override; 91 | }; 92 | 93 | /** 94 | * End namespace 95 | */ 96 | } 97 | 98 | -------------------------------------------------------------------------------- /php_script.h: -------------------------------------------------------------------------------- 1 | /** 2 | * PhpScript.h 3 | * 4 | * Class that is exposed to javascript and that can be used to parse 5 | * a script, and evaluate it multiple times. It can either be constructed 6 | * using "$script = new JS\Script($sourcecode)" or via 7 | * "$script = JS\Context::compile($source)". 8 | * 9 | * @copyright 2015 - 2025 Copernica B.V. 10 | */ 11 | 12 | /** 13 | * Include guard 14 | */ 15 | #pragma once 16 | 17 | /** 18 | * Dependencies 19 | */ 20 | #include 21 | #include "script.h" 22 | 23 | /** 24 | * Start namespace 25 | */ 26 | namespace JS { 27 | 28 | /** 29 | * Class definition 30 | */ 31 | class PhpScript : public Php::Base 32 | { 33 | private: 34 | /** 35 | * Shared pointer to the actual core data 36 | * @var std::shared_ptr 37 | */ 38 | std::shared_ptr _core; 39 | 40 | /** 41 | * The actual script 42 | * @var std::optional