How to override a PHP function? - php

UberCart for Drupal has some difficulties with currencies. However, by overriding "uc_currency_format", you can at least do some background calculation to give you a good estimate of the converted value. However, as it's part of UberCart Core, you can't edit the file, So you risk losing your code after every update. Also, this function does not have a hook!
That means the only that I can think of dealing with this, is having a module that overrides the function. So my question is...
Is there a way to override an existing PHP function? For example, I have:
function uc_currency_format($value, $sign = TRUE, $thou = TRUE, $dec = NULL)
{
// dont do this
}
But when this gets called, I want it to instead execute this
function uc_currency_format_rewrite($value, $sign = TRUE, $thou = TRUE, $dec = NULL)
{
// do this
}
Is that possible?

It seems to be one of those very rare times you need to hack the core code.
When it comes to this, I try to limit the impact at minimum like this:
Rename original function. In your case, you would go with something like 'uc_currency_format_ORIGINAL'
In your custom module, rename your 'uc_currency_format_rewrite' into 'uc_currency_format'
This way, you will have your own code running.
At next update, you will see in your testing environment (always better to test, before applying updates to production sites) a duplicate function name fatal error. If your hook has not been implemented yet, you will rename the original fuction, again.
This method is not defined in the best practice, of course. Use it at your own risk.

No it is not possible to override a function in PHP. Drupal 7 does not use Zend (rename_function(), override_function()) or OOP in modules. So you could only ask the maintainer for a new hook.
Maybe you could write a patch, which provides this hook and ask the maintainer for implementing it.

Related

Yii2 $model->_attributes assignment does not work in new version

I inherited a project that was created with Yii2, ver. 2.0.4, with the task to update said project to a more current version of Yii2 (2.0.15) because of the incompatibility of the older one with PHP 7.2+.
I noticed that there is a lot of use of assigning arrays to a model:
$model->_attributes = $array;
With the new version this results in an exception
'yii\base\UnknownPropertyException' with message 'Setting unknown property: app\models\model::_attributes'
For the time being I created a workaround with the following function:
function customSetAttributes(&$model, $array) {
foreach($model->attributeLabels() as $model_key => $model_label) {
if(!isset($array[$model_key])) continue;
$model->$model_key = $array[$model_key];
}
}
Also, the getter function now has a similar issue.
What I would like to know:
Was this type of assignment never intended in the first place (and I just haven't found the previous developer's code that enables it)? I skimmed over the Yii2 changelog but didn't notice anything related.
Is there a way to "salvage" the previous behaviour so I don't have to replace each occurence with my workaround function?
ActiveRecord::$_attributes was always private and never should be used in this way. I guess that previous developer edited framework core files in vendor directory and make this property protected/public.
You may try to emulate this behavior by creating virtual attribute using getter and setter:
public function get_attributes() {
return $this->getAttributes();
}
public function set_attributes($values) {
$this->setAttributes($values, false);
}
But this will not always work and it is more like an ugly hack to make crappy code work. I strongly suggest to fix code to use setAttributes() instead of _attributes.
Also you should compare yii2 package from vendor directory with source from https://github.com/yiisoft/yii2-framework/releases/tag/2.0.4 - you may find more places where core was edited.

PHP/Wordpress, Childthemes and functions already defined

I'm wondering (I have full access to the serve in case it's a php.ini setting) if there's anyway to "disable parsing of functions if a function was already defined" instead of throwing an error/notice about it?
For example /www/main/deep/file/file.php has:
function homepage_filter_get_map () {
// example1
$generic_filter_array = td_generic_filter_array::get_array();
}
and in /www/main-child/custom.php has, which is called/included/parsed before the file above:
function homepage_filter_get_map () {
//do nothing
}
Essentially, I'm looking for a way to suppress any and all errors outputting about already defined functions while silently ignoring functions that might have the same exact name, but already parsed/defined.
My problem is that the theme I'm using doesn't have full support for Wordpress child themes, just loop files mainly. I know I can just tweak the original files but I want the ability to be able to keep the theme updated without erasing all of our custom tweaks every-time.
Note: Yes, I know you can conditionally call functions, but again I'm looking for a way to do this without editing any of the "main" files since any tweaks done to those get overwritten when updating the parent theme.
if(!(function_exists('homepage_filter_get_map'))){
function homepage_filter_get_map(){
//do code
}
}
This checks if the function 'homepage_filter_get_map' exists. If it doesn't, create the function.

unrequire a file once it's been required in PHP

Suppose I do
require('lol.php');
whereby lol.php contains the following function declaration
function lolfunc(){
}
is it possible to "unrequire" lol.php such that I can then require another file
require('lol2.php');
whereby lol2.php contains a function with the same name previously declared in lol.php:
function lolfunc(){
echo "this is lol2 biyotch";
}
and have lolfunc() be the one declared in lol2.php? eg if I call lolfunc() it'll echo "this is lol2 biyotch"??
My answer: don't do that.
Try to work with the original author to include the functions you need into a patched version of the old code and then use that patched version everywhere.
If you can't do that, find out how big a job it would actually be to update all the code to new version. Start with white-box analysis: see what's changed in terms of interfaces, data structures et al. Then examine the calling code to see whether the caller cares about any of the things that have changed.
If you can't even do that, use namespacing or some other form of wrapping so that you can include both libs. However, make sure any initialisaton or setup is done on both libs!
What you should do, is to include the required (sets) of files based on conditional clauses, instead of trying to "unrequire".
So:
if($flag_use_oldver)
{
include("oldver.php");
}
else
{
include("newver.php");
}
If you want a more sophisticated solution, of course you could try to have a wrapper class hierarchy that will extend/override as required, but I think that is a bit over-engineering for a pretty straightforward problem statement.

Reloading a Class

I have a PHP daemon script running on the command line that can be connected to via telnet etc and be fed commands.
What it does with the command is based on what modules are loaded, which is currently done at the start. (psuedocode below for brevity)
$modules = LoadModules();
StartConnection();
while(true){
ListenForCommands();
}
function LoadModules(){
$modules = Array();
$dir = scandir("modules");
foreach($dir as $folder){
include("modules/".$folder."/".$folder.".php");
$modules[$folder] = new $folder;
}
}
function ListenForCommands(){
if(($command = GetData())!==false){
if(isset($modules[$command])){
$modules[$command]->run();
}
}
}
So, an example module called "bustimes" would be a class called bustimes, living in /modules/bustimes/bustimes.php
This works fine. However, I'd like to make it so modules can be updated on the fly, so as part of ListenForCommands it looks at the filemtime of the module, works out if it's changed, and if so, effectively reloads the class.
This is where the problem comes in, obviously if I include the class file again, it'll error as the class already exists.
All of the ideas I have of how to get around this problem so far are pretty sick and I'd like to avoid doing.
I have a few potential solutions so far, but I'm happy with none of them.
when a module updates, make it in a new namespace and point the reference there
I don't like this option, nor am I sure it can be done (as if I'm right, namespaces have to be defined at the top of the file? That's definitely workaroundable with a file_get_contents(), but I'd prefer to avoid it)
Parsing the PHP file then using runkit-method-redefine to redefine all of the methods.
Anything that involves that kind of parsing is a bad plan.
Instead of including the file, make a copy of the file with everything the same but str_replacing the class name to something with a rand() on the end or similar to make it unique.
Does anyone have any better ideas about how to either a) get around this problem or b) restructure the module system so this problem doesn't occur?
Any advice/ideas/constructive criticism would be extremely welcome!
You should probably load the files on demand in a forked process.
You receive a request
=> fork the main process, include the module and run it.
This will also allow you to run several commands at once, instead of having to wait for each one to run before launching the next.
Fork in php :
http://php.net/manual/en/function.pcntl-fork.php
Tricks with namespaces will fail if module uses external classes (with relative paths in namespace).
Trick with parsing is very dangerous - what if module should keep state? What if not only methods changed, but, for example, name of implemented interface? How it will affect other objects if they have link to instance of reloaded class?
I think #Kethryweryn is something you can try.

Is there an alternative to the "filter_var" function in php when using HHVM?

I have been recently playing around with HHVM. Went through a lot of trouble getting it to work on my computer. I know that not all PHP functions are available. As a test, I am writing a new website using it instead of using my current code. I ran into a problem when trying to use
filter_var($var,FILTER_SANITIZE_URL);
From the error.log file, it turns out that this function is undefined. Is the filter_var function not available for use in HHVM or am I just doing something wrong here. I like to keep things DRY, this would mean I have to do a lot more validation than I expected.
filter_var is now implemented in hhvm. Open github issues if you have any problems with it.
This function appears to not have been implemented on HHVM See http://comments.gmane.org/gmane.science.linguistics.wikipedia.technical/70038
An option if you want to rely on this functionality with the hopes that it will enter the fold is to polyfill it in (partial implementation to inspire the motivated).
if (!function_exists("filter_var")){
// define the constants used by the function
define("FILTER_VALIDATE_EMAIL", "email");
function filter_var(){
$args = func_get_args();
// $args[1] is the filter type (second parameter)
switch ($args[1]){
case FILTER_VALIDATE_EMAIL:
if (preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/", $args[0])?$args[0]:false;
break;
}
}
}

Categories