I am trying to implement a simple add_hook function using PHP but am coming across some trouble with updating the global array I have to store available/set hooks. If I use add_hook from main.php it works fine and I can add as many hooks as needed, but if doing the same thing from an included file, the $hooks var only updates from within the function.
main.php
require dirname(__FILE__) . '/includes/functions.php';
$hooks = array();
function add_hook($hook_name, $function)
{
global $hooks;
$hooks[$hook_name] = $function;
}
add_hook("hook_name", "some_function");
function execute_hook($hook_name)
{
global $hooks;
foreach ($hooks[$hook_name] as $function) {
if (function_exists($function[0])) {
call_user_func($function[0]);
}
}
}
function execute_hooks($hook_name)
{
global $hooks;
print_r($hooks);
if (array_key_exists($hook_name, $hooks)) {
execute_hook($hook_name);
}
}
print_r($hooks);
// Array ( [hook_name] => some_function )
// missing the array added from functions.php
functions.php
add_hook("build_admin_menu", "hd_modify_menu");
If I print_r at the end of the add_hook function, it will print the added hook, but I think the problem is that the "global" $hooks does not seem to update with the add_hook call from functions.php.
Can anyone explain to me why this is happening and what I can do to fix? Much appreciated!
Please check you code
require dirname(__FILE__) . '/includes/functions.php';
// $hooks = array();
The $hook array is reassigned after including functions.php . you need to comment/remove it. as shown above.
Hope this will help you
Related
I'm currently doing a beginner course in coding, mainly focusing on PHP and in one exercise we're changing our code from including a template through normal namespacing to instead including it via a function so that we can use output buffering. To make it work we are using extract() and although I understand how extract works, I struggle to see why we need to use it to make include work. Before running it via the function, we didn't need to send in or extract new variables. Is someone able to explain the reasons behind this?
Here's what the function looks like:
const TEMPLATE_EXTENSION = '.phtml';
const TEMPLATE_FOLDER = 'templates/';
const TEMPLATE_PREFIX = 'cart_view_';
function display($template, $variables, $extension = TEMPLATE_EXTENSION) {
extract($variables);
ob_start();
include TEMPLATE_FOLDER . TEMPLATE_PREFIX . $template . $extension;
return ob_get_clean();
}
Here's how we call it:
<?php echo display('user', ['users' => $users, 'cart' => $cart]); ?>
<?php echo display('item', ['new_item' => $new_item]); ?>
<?php echo display('items', ['cart' => $cart]); ?>
And here's what's in the templates we're including:
<h2>New Item</h2>
<p><?php printf($new_item['name']);?> is $<?php printf($new_item['price']);?></p>
<h2>User: <?php printf($cart['user']); ?></h2>
<p>ID: <?php printf($users[$cart['user']]['id']); ?></p>
<p>Email: <?php printf($users[$cart['user']]['email']); ?></p>
<h2>Cart</h2>
<?php foreach ($cart['items'] as $item) {
printf("<p>%s is $%d</p>\n", $item['name'], $item['price']);
} ?>
The variables are definied in another file which is already included in the index.
Previously before using the function to buffer, all we needed was this:
<?php include 'templates/cart_view_user.phtml'; ?>
<?php include 'templates/cart_view_item.phtml'; ?>
<?php include 'templates/cart_view_items.phtml'; ?>
Functions do have their own variable scope. So when you created the display() function, it doesn't see what variables are available outside of it. See more on Variable Scopes in PHP. You are using extract() in order to convert an array into variables in the scope where you are calling it from eg: in the function.
Your first solution was probably something like this (I'm making up the variables):
$users = [];
$cart = [];
// you are including the template in the same scope as the variables are defined, aka it will "see"/have access to those variables
include 'templates/cart_view_user.phtml';
Now you have refactored your code and moved the including logic in a function. This function has its own local scope.
$users = [];
$cart = [];
function display($template, $variables, $extension = TEMPLATE_EXTENSION) {
// you don't have access to $users and $cart here as those are defined in the global scope
extract($variables); // <-- after this call you will have new variables created in the local function scope based on your $variables array
ob_start();
include TEMPLATE_FOLDER . TEMPLATE_PREFIX . $template . $extension;
return ob_get_clean();
}
So now you would have two options if you know what variables you want to make available inside the function you could use the global keyword, but I would discourage using it, it could lead to weird bugs when you don't understand why your variables being changed (ps later you will move onto using classes and you won't have the headache of global variables).
// you can drop $variables from the function signature
function display($template, $extension = TEMPLATE_EXTENSION) {
global $users, $cart, $new_item;
// no extract needed, but please try not using global, it can lead to weird bugs
ob_start();
include TEMPLATE_FOLDER . TEMPLATE_PREFIX . $template . $extension;
return ob_get_clean();
}
Or you can use the extract to create variables in the function's scope so when you include the files they will have access to those variables. On thing to note here that extract will use the array keys as the variable names it is creating. That's why you are passing in display('user', ['users' => $users, 'cart' => $cart]); after this call, extract will create a $users and a $cart variable inside the function call. If you would call it with different array keys, like: display('user', ['u' => $users, 'c' => $cart]); the included file would complain that it can't find the variables $users and $cart.
I hope this helped, feel free to ask more if I wasn't clear anywhere.
I am well aware that globals are evil and this is an issue I will deal with later. It's not my codebase, but I've been assigned some cleaning up tasks.
Trying to smarten up a codebase, I decided to implement simple routing by using a package known as AltoRouter - I've worked with it before and it has worked fine for my purposes.
Now, the codebase is using a large amount of variables declared in the global scope. Usually, these variables are then fetched by using the globalkeyword. But for some reason, this doesn't work when I'm working inside a closure.
Consider this simple routing example:
<?php
require 'vendor/autoload.php';
$router = new AltoRouter();
$router->map('GET', '/shops/[i:id]', function($id) {
$_GET['shop_id'] = $id;
require 'go_to_shop.php';
});
$match = $router->match();
if( $match && is_callable( $match['target'] ) ) {
call_user_func_array( $match['target'], $match['params'] );
}
This calls my closure that sets a variable and requires a file.
This produces an error:
Fatal error: Call to a member function get() on null in
/vagrant/Core/CampaignHandler.php on line 71
Now, the code being called doing this is the following (line 70-71):
// Inside a method
global $serviceContainer;
$dispatcher = $serviceContainer->get("dispatcher");
The $serviceContainer is being declared by including a file early on:
$serviceContainer = new ServiceContainer();
$serviceContainer->set("dispatcher", new EventDispatcher());
Basically, if I move the contents of the closure outside of the closure, everything works perfectly - but as soon as I'm doing it from inside the closure, all variables accessed via the global scope is empty - and I have no idea as to why.
I've tried using use on the closure, this didn't work either.
I'm mostly looking for an explanation rather than a solution.
Globals are evil for a reason. You get the error because the global is not initialized at the time when function is being called. The mess of globals and requires is the exact issue and you are already trying to deal with it.
There is no problem to use globals in closure per se. This example works perfectly fine:
global.php:
<?php
class Foo {
public function bar() { return 'bar';}
}
$foo = new Foo;
test.php:
<?php
require 'global.php';
$test = function($param) {
global $foo;
echo $param, $foo->bar();
}
call_user_func_array($test, ['baz']);
so php test.php outputs bazbar;
I'm pretty sure that the $serviceContainer variable does not exist in the global scope, but the question leaves that part out.
Can't you pass the container to the anonymous function using a use( $serviceContainer ) statement? That'd be a far cleaner solution then having to rely on globals.
function($id) use( $serviceContainer ) {
$_GET['shop_id'] = $id;
require 'go_to_shop.php';
}
Off-topic: not sure what you're doing with that id variable later on and why you're putting it back into the $_GET variable like that, but please be careful.
Please check the manual for anonymus functions–also known as closures, which in real are objects.
http://php.net/manual/en/functions.anonymous.php
Theses callables have specific functions for extending their scopes.
See: Example #3 Inheriting variables from the parent scope.
$message = 'hello';
// No "use"
$example = function () {
var_dump($message);
};
$example();
// Inherit $message
$example = function () use ($message) {
var_dump($message);
};
$example();
Sure you want to assign a value to the $_GET global var?
$_GET['shop_id'] = $id;
The shop ID in your route you can extract from altorouter parameters. (See documentation.)
$router->map( 'GET', '/', function() { .. }, 'home' );
// assuming current request url = '/'
$match = $router->match();
/*
array(3) {
["target"] => object(Closure)#2 (0) { }
["params"] => array(0) { }
["name"] => 'home'
}
*/
Or if you want to store an ID for a session use $_COOKIES or $_SESSION global variables.
In my WP plugin, I need to call some functions of it in a different file,
/wp-content/plugins/wolf-jplayer/includes/jplayer-show.php has public function head_script( $id, $playlist_id, $songs, $in_popup, $autoplay = false ) { which I need to call in /wp-content/themes/twentlytwelve/index.php and then remove the function from it's original place since it's going to be in Index.php then. Is that possible? I tried everything and nothing worked.
#Niels my usage: used in wp-content/themes/twentytwelve/header.php
<?php
$Wolf_Jplayer_Show = new Wolf_Jplayer_Shows;
$Wolf_Jplayer_Show->head_script( $id, $playlist_id, $songs, $in_popup, $autoplay = false );
?>
the function is located in wp-content/plugins/wolf-jplayer/includes/jplayer-show.php
The function is possibly part of a class. If you keep the plugin activated you can create an instance of the class by;
<?php
$the_class = new Classname;
$the_class->function_name();
?>
I have a question regarding wp_rewrite, It doesn't display correctly,
I need to make this url
eduedu/wp-content/plugins/workwork/admin/templates/tcpdf/samp/flash.php
to
eduedu/generator
It works when using .htaccess, the problem is, that It doesnt work when place inside the plugin folder, the htaccess must be place in the root folder of wordpress. So I thought of using wp_rewrite.
Here is my code, I added this on the page when in it will redirect to
eduedu/wp-content/plugins/workwork/admin/templates/tcpdf/samp/flash.php
I'm not sure if its correct, any idea?
add_action('generate_rewrite_rules', 'cs_rewrite_rules');
add_filter('init', 'eduFlush');
function cs_rewrite_rules() {
global $wp_rewrite;
$new_non_wp_rules = array(
'^generator/?$' => 'eduedu/wp-content/plugins/workwork/admin/templates/tcpdf/samp/flash.php',
);
$wp_rewrite->non_wp_rules += $new_non_wp_rules;
}
function eduFlush(){
global $wp_rewrite;
$wp_rewrite->flush_rules();
}
Maybe something along these lines would help:
function site_router() {
global $url_array;
$url_array = explode("/",$_SERVER['REQUEST_URI']);
$route = $url_array[1];
$template_dir = 'wp-content/plugins/workwork/admin/templates/tcpdf';
switch($route) {
case 'generator':
load_template($template_dir.'/samp/flash.php');
die();
break;
}
}
add_action( 'send_headers', 'site_router');
It gets the current URL array (domain.com/url_array[1]), then it gets where you want to get the file from.
The switch gets the url, so domain.com/generator, and then loads in the template from the file you call.
You may need to change the url_array[1] to [2] if wordpress is in a sub-folder (domain.com/wp_root)
Code:
public function init()
{
global $cookie, $smarty, $cart, $iso, $defaultCountry, $protocol_link, $protocol_content, $link, $css_files, $js_files;
if(!session_id()){
if(!isset($_SESSION)){
session_start();
}
}
$cookie->id_cart=$_SESSION['pj_punchout_id'];
if (self::$initialized)
return;
self::$initialized = true;
$css_files = array();
$js_files = array();
Error:
Strict Standards: Creating default object from empty value in C:\xampp\htdocs\pjwebstoredev\classes\FrontController.php on line 82
I'm assuming that you're using Prestashop v1.4. The main problem I can see is that you've edited one of the core files, so most bets are off for the ability to support your code in the future. If you want to cleanly modify the behaviour of the core classes, then you should create an override called /override/classes/FrontController.php with the following contents:
class FrontController extends FrontControllerCore
{
function init()
{
// Your additional custom init code goes here
parent::init();
// And/or additional custom init code goes here
}
}
That's not the fundamental problems though, as we get to the next stage. The error you're seeing is because you are tring to use the global variable $cookie, but at a point in time before the variable is set to anything meaningful (the global cookie variable is actually initialised later in the very function you were modifying). Since you need to manipulate the cookie properties then you could try creating a temporary cookie object, use it to manipulate the user's cookie, then call the core code e.g.
class FrontController extends FrontControllerCore
{
function init()
{
if ( !session_id() ) {
if( !isset($_SESSION) ) {
session_start();
}
}
$cookieLifetime = (time() + (((int)Configuration::get('PS_COOKIE_LIFETIME_FO') > 0 ? (int)Configuration::get('PS_COOKIE_LIFETIME_FO') : 1)* 3600));
$cookie = new Cookie('ps', '', $cookieLifetime);
$cookie->id_cart=$_SESSION['pj_punchout_id'];
parent::init();
}
}