I am learning how to use hooks in mediawiki. I am also new to PHP.
General hook handler can be added by putting next line in to LocalSettings.php:
$wgHooks['event'][] = 'function';
Suppose I wrote myfunction in my.php file. How can I point to this function from LocalSettings.php
Edit. I have written some function in my.php file. How to refer to this function?
I should tell mediawiki where find this function.I don't know how to do it.
Should I write '$wgHooks['event'][] = 'my.php:function'. Or I should include my.php file to LocalSetting and then just write '$wgHooks['event'][] = 'function'
As the docs say, you need to push a string with your function name (or an array of strings etc.) to the hook array.
AFAIK, when triggering the hook they will be invoked with call_user_func(). So, it will depend on your function declaration in the my.php file. With a myfunction, it should be
$wgHooks['event'][] = 'myfunction';
from what i read from the mediawiki docs, you need to create an extension, and in your extension you install your hook. in my.php you will write:
// $wgHooks is a global variable
$wgHooks['event'][] = 'function';
Hope i understand correctly
Extension docs
http://www.mediawiki.org/wiki/Manual:Extensions
I am not sure I understand completely what you want, but if you want to call a user defined function you can use call_user_func which takes as argument the name of your function. You have to include the file so the function will be available.
call_user_func('myfunction ', array());
You refer to functions via callbacks in PHP. A callback is one of these:
a function name: 'myFunc'
an array containing a class name and a function name: array('MyClass', 'myFunc')
an array containing an object and a function name: array($myObj, 'myFunc')
an anonymous function (which is technically a Closure object): function($x, $y) { /* PHP code */ } (this is PHP 5.3+ only, but so are recent versions of MediaWiki)
Invoking these callbacks via call_user_func($callback, $arg1, $arg2) will be equivalent to the following, respectively:
myFunc($arg1, $arg2);
MyClass::myFunc($arg1, $arg2);
$myObj->myFunc($arg1, $arg2);
executing the body of the anonymous function, replacing $x and $y with $arg1 and $arg2
If this code would fail (e.g. you use the first version, and the function myFunc is not loaded), the callback will also fail. You can use autoloading with the second form, in MediaWiki that is normally done via $wgAutoloadClasses:
// in MyExtension.php
$wgHooks['event'][] = array('MyExtension', 'myEventHandler');
$wgAutoloadClasses['MyExtension'] = dirname(__FILE__) . 'MyExtension.body.php';
// in MyExtension.body.php
class MyExtension {
public function myEventHandler($p1, $p2) {
// do stuff
}
}
That way, you can load MyExtension.php (which is a small file with configuration settings only) from LocalSettings.php, and MyExtension.body.php (which has all the code) only gets loaded in those requests which actually use your extension.
Related
I'm playing with PHP and some functional style programming.
I'm using the Functional-PHP library but question is generic to PHP (I'm using 7.2).
I try to create a callable from an imported function but what I get is
TypeError: Failed to create closure from callable: function 'pick' not found or invalid function name
Sample code:
use function Functional\pick;
class A
{
public function execute()
{
$pick1 = \Closure::fromCallable('pick');
}
}
PHP use statements define an alias for the rest of the file, but they won't affect a string referencing an imported function or class.
When you say
use function Functional\pick;
it means that in that file, you can call the Functional\pick function just using pick(...). But if you're using a string to reference it then PHP doesn't know to expand the alias.
The quickest way to resolve this is just to use the fully qualified function name when calling fromCallable:
$pick1 = \Closure::fromCallable('Functional\pick');
echo get_class($pick1);
Closure
Alternatively, if you really wanted to use the alias, you could wrap the call a level deeper with another anonymous function:
use function Functional\pick;
$pick1 = \Closure::fromCallable(function (...$args) { return pick(...$args); });
But that's a lot messier, in my opinion at least.
Edit: There's some decent discussion around this in this recent thread in php-externals
I currently have this PHP code;
private function generateSpecialPage(){
require_once("/view/pages/special.php");
}
Special.php is a php file mostly filled with html. I'm trying to obtain the name of the current function from inside special.php.
If I echo the magic constant FUNCTION before the require, it echoes "generateSpecialPage", which is what I want. However, if I echo FUNCTION from special.php, it echoes nothing.
I'm able to get the current class' name from inside special.php using get_class($this), I was wondering if there was an equally elegant solution for the current method.
You should probably re-organize your code to change require_once("/view/pages/special.php") into a separate function and pass in the function name.
If you call generateSpecialPage() a second time, it won't do anything. You could get around this by changing it to require(), but then you're loading the file every time which is unnecessary.
The required file lives in the same scope as the function that required it.
So you can simply store to a variable before requiring the file:
private function generateSpecialPage() {
$caller = __FUNCTION__;
require_once '/view/pages/special.php';
}
then in special.php you have a regular variable $caller:
<?= "required by {$caller}" ?>
A general purpose call stack inspector will let you look back to fetch the caller function, class or object, file, and line. The one I use as part of my framework looks like, in essence:
function caller($offset = 0) {
return (new \Exception)->getTrace()[1+$offset];
}
Using this in your special.php will yield the desired result:
<?php echo caller(1)['function']; ?>
The call stack at that point is special.php -> require -> doSomethingSpecial, so we use the 1 offset to skip passed the require and get the doSomethingSpecial frame.
However, you might consider refactoring your view to receive parameters, rather than taking environmental cues. A general purpose view loader would go something like this:
function render($template, array $params = []) {
extract($params);
require $template;
}
which would then have a template that looked liked:
<?php echo "Hello {$caller}" ?>
and could be called like:
private function doSomethingSpecial() {
render('special.php', [ 'caller' => __FUNCTION__ ]);
}
I realize this is more typing than might be desired, but it affords more flexibility in the long-term, as it decouples the view from the caller.
I have tested the following and it doesn't work. Is there a similar way of achieving this?
A compiling class which formulates a template:
private function include_header () {
function _metadata () {
// Metadata compiler
return $metadata;
}
include (path . "header.php");
}
header.php
<html>
<head>
<?php
_metadata ();
?>
<title>Hello Stackoverflow!</title>
</head>
<body>
...
The _metadata (); function is only available within the include_header (); function - nowhere else in the class. I don't want to have to write $this->_metadata (); in the template file only _metadata ();. Is that achievable?
Alternatively one could create an external file, for example functions.php which has the underscore functions specifically for the template - but I'd much rather see if the original query is possible.
Thank you in advance.
From the manual:
All functions and classes in PHP have the global scope - they can be called outside a function even if they were defined inside and vice versa.
So, essentially, you can't do what you're trying to do. _metadata will always be a global function if it is defined with the function _metadata() { syntax. The normal function syntax only supports global functions.
However, PHP 5.3 introduces something called "anonymous functions". These can be assigned to a variable. Since variables can be scoped to a function scope (they are only global if you explicitly say so), you can limit them in this way.
For instance:
$_metadata = function() {
// Metadata compiler
return $metadata;
};
This could then be called in header.php with:
<?php $_metadata(); ?>
Note that this is less stable: it is, for instance, possible to redefine the variable to be a different anonymous function – or indeed not a function at all. The safest solution, ultimately, is to use a templating language that solves these issues for you. If not, you can either accept that you'll have to use global functions or anonymous functions.
You can't define nested function in PHP. I mean, you can, but there's no purpose since they'll have a global scope and therefore they will be callable from any point of the script.
But you actually don't need to define a _metadata() function, you can just execute all the function logic inside include_header() and then store everything in the variable $metadata, which will be printed in the include with a simple <?php echo $metadata; ?>.
If you really need to define a function you can use the function create_function() or (just for PHP > 5.3) an anonymous function. Since they will be encapsulated inside a variable, which has a function scope, they won't be available outside.
In my bootstrap.php I have many _initX() functions, and some of them may contain code that depends on code in the previous initX
protected function _initAutoloading() { }
protected function _initViewInitializer() { }
protected function _initVariables() { }
So my question, are these _init functions guaranteed to be executed in the exact order they've been declared?
EDIT - To provide a more direct answer to your question, I would say that they probably will be since the code uses ReflectionObjects::getmethods() or get_class_methods depending on your PHP version, so I believe those will return the function in order but there is nothing in the PHP docs or Zend docs that guarantee this will always be the case, so I would not consider this a supported feature.
You can pass the names of the resource functions you want/need to call as part of the bootstrap call: $bootstrap->bootstrap(array('foo', 'bar')); instead of not passing anything and let the Zend Application call them all automatically in which you are not sure of the order.
If you have dependencies in between your bootstrap resources however, I suggest you look at Resource plugins which will allow you to separate your code in different classes and easily call $bootstrap('foo') from within your 'bar' resource plugin code (though you can do so with the _init*() functions as well)
Another benefit of resource plugins is they can be shared with other bootstrap files if you need to and they are easier to test than _init*() functions.
Make sure you read theory of operation document from the Zend Application doc
If you really need them invoked in a particular order, you should use a helper list:
var $init_us = array(
"_initAutoloading",
"_initViewInitializer",
"_initVariables",
);
function __construct() {
foreach ($this->init_us as $fn) {
$this->{$fn}();
}
}
To use that construct in ZF you could rename the example __construct into _initOrderedList and your custom _initFunctions into _myinit... or something.
Read the manual. There are a section called Dependency Tracking :
If a resource depends on another resource, it should call bootstrap() within its code to ensure that resource has been executed. Subsequent calls to it will then be ignored.
Here is sample code :
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initRequest()
{
// Ensure the front controller is initialized
$this->bootstrap('FrontController');
// Retrieve the front controller from the bootstrap registry
$front = $this->getResource('FrontController');
$request = new Zend_Controller_Request_Http();
$request->setBaseUrl('/foo');
$front->setRequest($request);
// Ensure the request is stored in the bootstrap registry
return $request;
}
}
You don't have to rely on the order.
Can you declare a function like this...
function ihatefooexamples(){
return "boo-foo!";
};
And then redeclare it somewhat like this...
if ($_GET['foolevel'] == 10){
function ihatefooexamples(){
return "really boo-foo";
};
};
Is it possible to overwrite a function that way?
Any way?
Edit
To address comments that this answer doesn't directly address the
original question. If you got here from a Google Search, start here
There is a function available called override_function that actually fits the bill. However, given that this function is part of The Advanced PHP Debugger extension, it's hard to make an argument that override_function() is intended for production use. Therefore, I would say "No", it is not possible to overwrite a function with the intent that the original questioner had in mind.
Original Answer
This is where you should take advantage of OOP, specifically polymorphism.
interface Fooable
{
public function ihatefooexamples();
}
class Foo implements Fooable
{
public function ihatefooexamples()
{
return "boo-foo!";
}
}
class FooBar implements Fooable
{
public function ihatefooexamples()
{
return "really boo-foo";
}
}
$foo = new Foo();
if (10 == $_GET['foolevel']) {
$foo = new FooBar();
}
echo $foo->ihatefooexamples();
Monkey patch in namespace php >= 5.3
A less evasive method than modifying the interpreter is the monkey patch.
Monkey patching is the art of replacing the actual implementation with a similar "patch" of your own.
Ninja skills
Before you can monkey patch like a PHP Ninja we first have to understand PHPs namespaces.
Since PHP 5.3 we got introduced to namespaces which you might at first glance denote to be equivalent to something like java packages perhaps, but it's not quite the same. Namespaces, in PHP, is a way to encapsulate scope by creating a hierarchy of focus, especially for functions and constants. As this topic, fallback to global functions, aims to explain.
If you don't provide a namespace when calling a function, PHP first looks in the current namespace then moves down the hierarchy until it finds the first function declared within that prefixed namespace and executes that. For our example if you are calling print_r(); from namespace My\Awesome\Namespace; What PHP does is to first look for a function called My\Awesome\Namespace\print_r(); then My\Awesome\print_r(); then My\print_r(); until it finds the PHP built in function in the global namespace \print_r();.
You will not be able to define a function print_r($object) {} in the global namespace because this will cause a name collision since a function with that name already exists.
Expect a fatal error to the likes of:
Fatal error: Cannot redeclare print_r()
But nothing stops you, however, from doing just that within the scope of a namespace.
Patching the monkey
Say you have a script using several print_r(); calls.
Example:
<?php
print_r($some_object);
// do some stuff
print_r($another_object);
// do some other stuff
print_r($data_object);
// do more stuff
print_r($debug_object);
But you later change your mind and you want the output wrapped in <pre></pre> tags instead. Ever happened to you?
Before you go and change every call to print_r(); consider monkey patching instead.
Example:
<?php
namespace MyNamespace {
function print_r($object)
{
echo "<pre>", \print_r($object, true), "</pre>";
}
print_r($some_object);
// do some stuff
print_r($another_object);
// do some other stuff
print_r($data_object);
// do more stuff
print_r($debug_object);
}
Your script will now be using MyNamespace\print_r(); instead of the global \print_r();
Works great for mocking unit tests.
nJoy!
Have a look at override_function to override the functions.
override_function — Overrides built-in
functions
Example:
override_function('test', '$a,$b', 'echo "DOING TEST"; return $a * $b;');
short answer is no, you can't overwrite a function once its in the PHP function scope.
your best of using anonymous functions like so
$ihatefooexamples = function()
{
return "boo-foo!";
}
//...
unset($ihatefooexamples);
$ihatefooexamples = function()
{
return "really boo-foo";
}
http://php.net/manual/en/functions.anonymous.php
You cannot redeclare any functions in PHP. You can, however, override them. Check out overriding functions as well as renaming functions in order to save the function you're overriding if you want.
So, keep in mind that when you override a function, you lose it. You may want to consider keeping it, but in a different name. Just saying.
Also, if these are functions in classes that you're wanting to override, you would just need to create a subclass and redeclare the function in your class without having to do rename_function and override_function.
Example:
rename_function('mysql_connect', 'original_mysql_connect' );
override_function('mysql_connect', '$a,$b', 'echo "DOING MY FUNCTION INSTEAD"; return $a * $b;');
I would include all functions of one case in an include file, and the others in another include.
For instance simple.inc would contain function boofoo() { simple } and really.inc would contain function boofoo() { really }
It helps the readability / maintenance of your program, having all functions of the same kind in the same inc.
Then at the top of your main module
if ($_GET['foolevel'] == 10) {
include "really.inc";
}
else {
include "simple.inc";
}
You could use the PECL extension
runkit_function_redefine — Replace a function definition with a new implementation
but that is bad practise in my opinion. You are using functions, but check out the Decorator design pattern. Can borrow the basic idea from it.
No this will be a problem.
PHP Variable Functions
Depending on situation where you need this, maybe you can use anonymous functions like this:
$greet = function($name)
{
echo('Hello ' . $name);
};
$greet('World');
...then you can set new function to the given variable any time
A solution for the related case where you have an include file A that you can edit and want to override some of its functions in an include file B (or the main file):
Main File:
<?php
$Override=true; // An argument used in A.php
include ("A.php");
include ("B.php");
F1();
?>
Include File A:
<?php
if (!#$Override) {
function F1 () {echo "This is F1() in A";}
}
?>
Include File B:
<?php
function F1 () {echo "This is F1() in B";}
?>
Browsing to the main file displays "This is F1() in B".