I have written a PHP library using the PHP 8.0 readonly keyword and then I realised that it would be good to support earlier versions of PHP such as 7.4.
I could easily remove the readonly keywords from my code but I don't want to do that -- they were put there for a reason!
Having a C background, I immediately thought of macros but PHP doesn't seem to have any. I've googled this answer for adding macro-like behaviour to PHP, but it looks like an overkill. And it's just for one file, and my library has 26 files at present.
Is there an easy way to make PHP 7.4 just ignore the readonly keyword and make my code cross-version? Something to the effect of this C code?
#if PHP_VERSION < 8
#define readonly /**/
#enif
Perphaps some composer build option that can pre-process files before packaging them up?
Out of the box, PHP does not include conditional compilation of the type you're hoping for.
One option would be to pre-process the source files on the fly, using a custom autoloader or Composer hook. The idea would be to let the normal code run to the point where it was going to include the file, then instead fetch its contents and manipulate it.
Note that this would not need to be a fully-functional macro system, you could just surround the code with some clear markers, like /* IF PHP 8 */ readonly /* END IF */ and match them with a simple regex pattern:
$php_code = file_get_contents($php_file_being_loaded);
if ( PHP_VERSION_ID < 80000 ) {
$php_code = preg_replace('#/\* IF PHP 8 \*/.*?/\* END IF \*/#s', '', $php_code);
}
eval($php_code);
Alternatively, you could run the pre-processing "offline", to automatically produce parallel versions of the library: one for PHP 8.0 and above, and a different one for PHP 7.4. Again, this could be as simple as the above, or you could use a tool like Rector which parses and rewrites normal PHP code (with no extra markers) according to set rules, including "downgrading" it to be compatible with a particular version of PHP.
PHP is not being compiled, therefore there's no compiler macros.
According to Backward Incompatible Changes it's a new keyword:
readonly is a keyword now. However, it still may be used as function name.
So you have two choices: a) don't re-assign it's value or b) maintain two versions.
most frameworks simply avoid using modern features for this reason alone (WordPress, Symfony, Laravel), but if you insist, your best bet is probably Composer, you can have a v1.x.x with composer.json
{
"require": {
"php": ">=7.4"
},
}
and a v2.x.x with composer.json
{
"require": {
"php": ">=8.0"
},
}
then when people do composer require lib, composer will automatically scan for and install the newest version of your library that is compatible with the local php version and composer.json-constraints (-:
the downside is that you'll have to maintain both v1 and v2 of your library for as long as you intend to support php 7.4 though..
another option is to have a loader like lib.php
if(PHP_MAJOR_VERSION >= 8){
require("lib_modern.php");
} else{
require("lib_legacy.php");
}
again with the downside of having to maintain both lib_modern and lib_legacy
Related
Is there a rename refactoring for PHP in Visual Studio Code? It is the only thing that stops me from fully move from NetBeans to VSC. Renaming is very importat for me (not search/replace).
There are
{
"command": "editor.action.changeAll",
"key": "ctrl+f2",
"when": "editorTextFocus && !editorReadonly"
}
and
{
"command": "editor.action.rename",
"key": "f2",
"when": "editorHasRenameProvider && editorTextFocus && !editorReadonly"
}
in the keybindings. F2 appears to work across all files, CTRL-F2 in the current file only.
Try this rename symbol instead of the standard find/replace. Rename is bound to F2 for all files, CTRL - F2 for current file, may require installing a php language extension.
There is an extension called Intelephense extension that provides expected functionality (in paid version as I remember). So I used it for several years in Visual Studio Code and it worked very well.
But I have finally moved to PhpStorm IDE for much more powerful refactoring capabilities and robust PHP support in general.
At the time of this posting, the only way I know of to do this is to buy PHP Intelephese Premium. The free version and the other PHP intellisense plugins do not support rename.
Maybe this would be helpfull
ext install marsl.vscode-php-refactoring
Install and use Comand line (Ctrl+P) and activate PHP: Rename local variable
Overview
In PHP 5.6 it seems that adding declare(ticks=1) and then using register_tick_function() would follow any includes and provide profiling information accordingly.
In PHP 7+ however it now seems I have to add declare(ticks=1) in every file. I use this for profiling every method call on a page load and don't now want to add this to each PHP file in my system (some I can't if they are in libraries).
I can't find anything in the docs about changes that were made to this.
Replication Steps
Create the below 2 files:
index.php
<?php
declare(ticks=1);
$count = 0;
register_tick_function('ticker');
function ticker() {
global $count;
$count++;
}
$foo = 'foo';
$bar = 'bar';
include dirname(__FILE__) . '/inc.php';
echo $count;
inc.php
<?php
#declare(ticks=1);
$baz = "baz";
$qux = "qux";
Results
Running php index.php in the terminal gives me:
PHP 5.6 - 7
PHP 7.0 - 5
With declare(ticks=1) uncommented in inc.php the results are:
PHP 5.6 - 8
PHP 7.0 - 8
Question
Is there any way to way to force it to follow includes and in a sense make it global in PHP 7+?
According to the PHP bug filed at https://bugs.php.net/bug.php?id=71448
Due to an implementation bug, the declare(ticks=1) directive leaked into different compilation units prior to PHP 7.0. This is not how declare() directives, which are per-file or per-scope, are supposed to work.
So in fact it was a bug that it ever did work as it did in PHP 5.6 and the correct implementation has been added in PHP 7.0. Unfortunately this means it will never work but at least there is an explanation.
There is an answer on the below question that shows how to achieve this in PHP 7+
Natively profile multiple scripts in PHP7
I am writing a php extension for a library. I have a generic swig file to build wrappers for the library. This has been very successful so far on Python.
A user is trying to build the library for php and I am trying to help out. I generated the code using swig, and I can build the .dll extension using Visual Studio. The problem is getting it into php. When I build php_mylib.dll, it needs to find mylib.dll and it can't.
So I am trying to build via the command line a la:
http://blog.benoitblanchon.fr/build-php-extension-on-windows/
I have put all the files to be compiled and the libraries needed (i.e. mylib64.lib and mylib64.dll) in a folder called mylib in the C:\php-src\ext folder with all the other extension folders.
The problem is I that I can't get the config.w32 file to communicate the location of mylib. Here is my config.w32 file (pretty standard -- you can see that I copied it from the curl config.w32 file):
// $Id$
// vim:ft=javascript
ARG_ENABLE("mylib", "mylib support", "no");
if (PHP_MYLIB != "no") {
if (CHECK_LIB("mylib64.lib", "mylib", PHP_MYLIB) &&
CHECK_HEADER_ADD_INCLUDE("mylib_cpp.h", "CFLAGS_MYLIB")
) {
EXTENSION("mylib", "mylib_c_wrap.cpp", true);
AC_DEFINE('HAVE_MYLIB', 1, 'Have mylib library');
// TODO: check for curl_version_info
} else {
WARNING("mylib not enabled; libraries and headers not found");
}
}
When I run buildconf and then configure --disable-all --enable-cli --enable-mylib it always shoots me the 'libraries and headers not found' warning from the script.
On Unix systems (config.m4) there appears to be a PHP_ADD_LIBRARY_WITH_PATH macro but I don't see any equivalent for windows. It seems like this is what I need.
I have also tried adding the full path to mylib into the system's path but to no avail. It seems like there might be an environment variable somewhere in the PHP build cosmos that needs to be set to find external dependencies, but I can't find any information about this.
It would also be good to do all this as a Visual Studio project -- easier for Windows users the world over. I have not seen anything on the web that looks like this.
By the way, I have successfully phpized this library using the same swig+phpize procedure on Linux (I followed this guide for the php part:http://www.sitepoint.com/install-php-extensions-source/) and it works beautifully.
In most cases you can use this info to build a PECL extension https://wiki.php.net/internals/windows/stepbystepbuild#building_pecl_extensions . Your config.w32 looks ok, though please note that that when depending on some additional library, usually it should be ARG_WITH(...) for semantics. Your lib stuff can be then put into the deps dir as in the wiki.
Another way could be passing --with-extra-libs and --with-extra-includes to configure. Those have to contain paths to *.lib and *.h dirs respectively. When using a static lib, that should be it, otherwise you'll need to place the corresponding *.dll onto the %path% for the ext to work.
Hope this helps, otherwise you can also gain some attention on the PHP mailing lists or specifically on #winphp-dev at freenode.
Thanks.
I am using wampserver to test & run wordpress code in my local computer. In order to run pthread, I have followed the following steps:
1) I got the pthread zip file from http://windows.php.net/downloads/pecl/releases/pthreads/0.44/
(My machine has php 5.3.13 and downloaded the php_pthreads-0.44-5.3-ts-vc9-x86.zip file from the above link).
2) Extracted the zip file. Moved the php_pthreads.dll to the C:\wamp\bin\php\php5.3.13\ext directory.
3) Moved pthreadVC2.dll to the C:\wamp\bin\php\php5.3.13 directory.
4) Then Opened C:\wamp\bin\php\php5.3.13\php.ini and added the code extension=php_pthreads.dll at the begining of the file.
But when I try to run the following code:
<?php
class My extends Thread {
public function run() {
printf("%s is Thread #%lu\n", __CLASS__, $this->getThreadId());
}
}
$my = new My();
$my->start();
?>
It gives me the following error:
Fatal error: Class 'Thread' not found in C:\wamp\www\wp-admin\includes\post.php on line 2
Can you please tell me how to install pthreads in my computer to use with php? and do I have to install any other software?
I've noticed that wampserver has php.ini in two separate places. One place is in the /wamp/bin/php/php5... directory, and the other place is in the /wamp/bin/apache/apache.../bin directory (where "..." represents version numbers). The two files need to be identical, because apparently both are loaded at different times by the overall wampserver boot-up procedure.
(Note I only discovered this recently, and may be well "behind the curve" of doing fancy things with wampserver --maybe everyone else has been dealing with both files for a long time. So I don't know if this simple thing will fix your problem; I came here looking for info, myself, regarding doing some multi-threading stuff. :)
One other thing. According to this page: www.php.net/manual/en/pthreads.requirements.php
PHP has to be compiled with "--enable-zts" in order for pthreads stuff to work. I have not been able to find any evidence that the PHP part of wampserver was compiled that way.
(months later)
Having decided I didn't really immediately need to do any threading stuff, I went on to do other things, until the need actually arose. I now can say that the version of PHP compiled into WampServer does support the "pthread" extension, although some set-up work is needed, first. The instructions I saw mentioned putting a couple of .dll files (after a download and unZip) into certain places, but that didn't work for me. Copying them to the \Windows\System32 directory did work. (Putting them into the \apache...\bin directory also works; there are some other php .dll files in there.)
After that, much like what you did, it is necessary to define a "class" that extends the "Thread" class, in order to actually do something in another thread. The "run()" function in the Thread class is "abstract", and needs to be "realized" as an actual function in the extended class. Then the "new" operator can create an "instance", an object of that specified class, for actual use. Here's the class that I needed:
//Purpose: Use another thread to run the code in another php file, after a delay
class xT extends Thread
{ var $fil, $tim;
function savWhatWhen($f="", $t=0)
{ $this->fil = $f; //save What, file to process
$this->tim = $t; //save When, delay before processing file
return;
}
function run()
{ ini_set('max_execution_time', 600); //600 seconds = 10 minutes
sleep($this->tim); //do a delay; beware of max-exec-time!
include($this->fil); //load file-to-process, and process it
return;
} }
That "savWhatWhen()" function was created specifically for this extension of the basic Thread class. Here's some code for using that class:
$TH = new xT(); //prepare a separate Thread
$TH->savWhatWhen("d:/wamp/myscripts/test.php", 45);//file-name and delay time
$TH->start(); //after delay, process file
//the code that does this can terminate, while OTHER thread is doing a delay
Note for anyone copying this code, you might need to make sure your "open_basedir" setting in the php.ini allows access to the specified file.
More months later: With lots of things being worked on, I haven't put a lot of time into using my pthread object. I did encounter a peculiarity that makes me wonder about whether or not I can actually use pthreads the way I had hoped. Here is what I have observed:
1. An initial php file is called by AJAX, to do something.
2. The PHP processor on the Web Server does that thing.
3. Various data is supposed to be echoed to the browser.
4. The initial php file calls for the creation of another thread, and terminates.
5. The browser does not yet receive the echoed data!
6. The PHP processor on the Web Server does the work delegated to the second thread.
7. When the second thread terminates, NOW the browser receives the echoed data!
At this writing I'm thinking I missed something. Perhaps I need to do some forceful "flush" stuff when the first thread ends, so that the browser can receive the echoed data and the user can do things while the PHP processor on the server is also doing things.
Check for extension_dir = "ext" in you php.ini file. Make sure it points to the folder where your extensions reside and make sure it's not commented (it has a semicolon ; in front of it)
You have to add a require_once() with the path of the Thread class before extending it (if your framework don't use an autoload class system)
I've encountered the same problem, in my case placing the pthreadVC2.dll in
..wamp\bin\apache\Apache2.4.4\bin
(instead of ..\wamp\bin\php\php5.4.16 as the guide in php.net instructs) solved the problem
Wamp server has a separate php.ini config file for the browser and for the cli.
To use the pthreads module in the browser with WAMP Server you need to copy the "pthreadVC2.dll" into the apache "bin" directory also.
You should now have he "pthreadVC2.dll" in both of these folders (if installed in default location):
C:\wamp\bin\php\php[x.x.xx]\bin
C:\wamp\bin\apache\apache[x.x.x]\bin
You will also need to update the php.ini file within the php bin directory AND the apache bin directory to include:
extension=php_pthreads.dll
This now means you can use pthreads in the browser and in the cli with wamp server
After encountering the same problem, I noticed that I have installed the wrong Pthread version (3.1.6 : requires PHP7+) which wasn't compatible with my PHP version (5.5.12). Solved the problem with Pthread version 0.0.44. An earlier version should probably work well.
Here is the download page for Pthread and the installation page. Be careful about the both php.ini location as mentioned above (Apache folder=for Browser, PHP folder=CLI).
This question already has answers here:
Where can I learn about PHP internals? [closed]
(4 answers)
Closed 1 year ago.
The community reviewed whether to reopen this question 11 months ago and left it closed:
Needs details or clarity Add details and clarify the problem by editing this post.
I know you can technically make PHP extension just by making a PHP file and using require_once.
But would it optimize the performance if you wrote an extension in C or C++.
If so, how would you make a "hello-world" for that?
I know you can technically make PHP extension just by making a PHP file and using require_once.
The base of this functionality is the include statement, which includes and evaluates the specified file. Extension isn't the right term, because you are just including another PHP script file. A PHP extensions provides additional functions to the language in form of a compiled module.
But would it optimize the performance, if you wrote an extension in C or C++.
Yes, it optimizes the performance. That's why PHP extensions like CPhalcon or YAF were written.
How to make a "Hello World" PHP Extension?
I will describe how you can build a "Hello World" PHP extension in five steps.
A Debian based OS is required, because we need to fetch some tools and dependencies with apt-get.
Step 1 - Setup Build Environment / Requirements
A PHP Extension is compiled C code. We need a shell (should already be installed), an editor (your choice), a compiler (here we'll use GCC), PHP itself and PHP development dependencies for the build.
sudo apt-get install build-essential php7.0 php7.0-dev
Step 2 - Config
We need to describe our extension and the files forming it in a basic configuration file:
File: config.m4
PHP_ARG_ENABLE(php_helloworld, Whether to enable the HelloWorldPHP extension, [ --enable-helloworld-php Enable HelloWorldPHP])
if test "$PHP_HELLOWORLD" != "no"; then
PHP_NEW_EXTENSION(php_helloworld, php_helloworld.c, $ext_shared)
fi
As you can see, the NEW_EXTENSION contains a C file: php_helloworld.c.
Step 3 - Code
Let's create the C code for our extension.
Firstly, we create a header file:
php_helloworld.h
// we define Module constants
#define PHP_HELLOWORLD_EXTNAME "php_helloworld"
#define PHP_HELLOWORLD_VERSION "0.0.1"
// then we declare the function to be exported
PHP_FUNCTION(helloworld_php);
Secondly, we create the source file:
php_helloworld.c
// include the PHP API itself
#include <php.h>
// then include the header of your extension
#include "php_helloworld.h"
// register our function to the PHP API
// so that PHP knows, which functions are in this module
zend_function_entry helloworld_php_functions[] = {
PHP_FE(helloworld_php, NULL)
{NULL, NULL, NULL}
};
// some pieces of information about our module
zend_module_entry helloworld_php_module_entry = {
STANDARD_MODULE_HEADER,
PHP_HELLOWORLD_EXTNAME,
helloworld_php_functions,
NULL,
NULL,
NULL,
NULL,
NULL,
PHP_HELLOWORLD_VERSION,
STANDARD_MODULE_PROPERTIES
};
// use a macro to output additional C code, to make ext dynamically loadable
ZEND_GET_MODULE(helloworld_php)
// Finally, we implement our "Hello World" function
// this function will be made available to PHP
// and prints to PHP stdout using printf
PHP_FUNCTION(helloworld_php) {
php_printf("Hello World! (from our extension)\n");
}
Step 4 - Build
Now, we are ready to build the extension.
First we prepare the build environment for a PHP extension:
phpize
Then we configure the build and enable our extension:
./configure --enable-php-helloworld
Finally, we can build it:
make
sudo make install
Step 5 - Test
To test our PHP extension, lets load the helloworld_php.so extension file and execute our function helloworld_php():
php -d extension=php_helloworld.so -r 'helloworld_php();'
Done :)
Building on Windows
If you try to build an Windows (YIKES!),
then you need to adjust the steps a bit:
Step 2 - Config
File: config.w32
ARG_ENABLE("helloworld", "helloworld support", "yes");
if (PHP_HELLOWORLD == "yes") {
EXTENSION("helloworld", "php_helloworld.c");
}
Step 4 - Build
Use nmake instead of make.
List of helpful resources:
A good book on the C programming language
https://wiki.php.net/internals/
https://wiki.php.net/internals/extensions
http://phpinternalsbook.com/
https://phpinternalsbook.com/php7/build_system/building_extensions.html
https://nikic.github.io/
http://jpauli.github.io/
https://github.com/php/php-src
http://www.slideshare.net/pierrej/extending-php-7-the-basics - explains basic argument handling
https://github.com/phplang/extension-tutorial - Materials for an Extension Writing Tutorial
Software written in C/C++ certainly does run faster than code in PHP. And you can write an extension in C/C++ and link it into PHP. The PHP manual covers this here: http://php.net/manual/en/internals2.php
The other answers give links to other tutorials for writing PHP extensions, and you can google for "PHP extension tutorial" to find more.
But whether this is the right thing to do in your app is another story. Most experts agree that PHP runs just fine, fast enough for 98% of applications. The instances where PHP isn't fast enough are not in general due to the language, but an inefficient application architecture that the programmer has created. That's a weakness that can't be remedied by rewriting parts of your app in C/C++.
Here's a tutorial on PHP extensions. Whether it will optimize the performance or not, it depends on what you are trying to wrap on your extension. But I would not write a PHP extension just for optimization purposes. I would write one if I have no choice. I.E. Wrapping a common C library to make it available directly in PHP...
i think (but no sure) you can do that by make an dll file and put it in ext folder which exist with php installation files.if my previous words is correct you can do that (dll) file in visual studio