Within a Drupal module callback function, there is a simple custom function that intakes an array.
The custom function executes correctly when I define the input array within the Drupal module callback function. However, when I define the input array at the root level (global), the custom function within the Drupal module callback function fails.
As a test, I made the custom function simply output the contents of the input array as a string. The first method outputs correctly while the second method does not have any output. Ideally, I'd like to define the array at the global level so that it can be used by other functions.
Thoughts?
<?php
// ** Placement of array for method 2
$mapping = array(
0 => "name",
1 => "match"
);
function mymodule_menu() {
$items = array();
$items['mymodule'] = array(
'title' => 'MyModule',
'page callback' => 'myModule_main',
'access callback' => TRUE,
'type' => MENU_NORMAL_ITEM
);
return $items;
}
function myModule_main() {
// ** Placement of array for method 1
$mapping = array(
0 => "name",
1 => "match"
);
$output = myFunction($mapping);
echo $output; // ** Returned to client side via AJAX
}
You need to "import" the global variable into the function's scope using the global keyword.
See http://php.net/manual/en/language.variables.scope.php#language.variables.scope.global
function myModule_main() {
global $mapping;
...
}
<?php
global $foobar;
$foobar = "text";
function myFunction() {
echo $GLOBALS["foobar"]; // Returns "text"
}
?>
Related
I have a private function to return an array of options, those options indicate a callback and other options such as template, form, etc. Here the code:
/**
* #return array
*/
private function options()
{
$options = [
'general' => [
'form' => GeneralConfigType::class,
'template' => 'general.html.twig',
'title' => 'ConfiguraciĆ³n General',
'ignoreFields' => ['slider', 'social'],
'uploadedFields' => [],
'callbacks' => ['generalData']
],
'business' => [
'form' => ConfigurationType::class,
'template' => 'business.html.twig',
'title' => 'ConfiguraciĆ³n de Empresa',
'ignoreFields' => [],
'uploadedFields' => ['image','favicon','login_icon','sidebar_icon'],
'callbacks' => ['businessImage']
],
];
return $options;
}
Now here is my doubt, in addition to indicate the function you have to execute in the key callback, Can I pass on the variables I'm going to need in that callback? I've tried several ways and they haven't worked.
Example:
Before:
'callbacks' => ['generalData']
After:
In this example I'm assigning the '$', but I could do it if the only string, I'm just looking for a way to pass to the callback the variables it needs and no more.
'callbacks' => ['generalData' => '$configurationData, $configuration, $form, $request']
And this code would be where everything would be executed in other method:
if (!empty($options[ 'callbacks' ])) {
foreach ($options[ 'callbacks' ] as $callback => $variables) {
$this->$callback($variables);
}
}
If I understand you correctly, you want to store the name of the variable in the array of options and then use that variable in the callback function.
When I've done this type of thing, I find it easier to just store the variable name as text and leave out the $ from the name stored in the array. I then use a variable variable when retrieving it.
Either way, I think you need a little more code on the execution side. One more loop:
if (!empty($options[ 'callbacks' ])) {
foreach ($options[ 'callbacks' ] as $callback => $variables) {
foreach($variables as $variable){ // extra loop to get the variables
$this->$callback[$$variable];
// This is where it gets tricky, and depends on how you wish to format.
// The variables are currently part of an array, thus the array notation
// above. By using the stored name only, and a variable variable, you
// should be able to get to the var you need
}
}
}
#jcarlosweb, what you need to do is very simple. The short answer is that it can be done using the [call_user_func_array()][1] method.
In the context of your example, the callbacks could be rearranges in the following way ...
'callbacks' => ['generalData' => [$configurationData, $configuration, $form, $request]
Basically, the array keys will be the name of the function to call, and the corresponding array values will be a array of the values of each parameter that is accepted but the callback function. Doing it this way is important because you need to capture the value of the parameters while they are in scope. And this will avoid using eval().
Using the callbacks can be as simple as ...
$options = options();
foreach ($options['callbacks'] as $callback => $params) {
$result = call_user_func_array($callback, $params);
// Do something with $result if necessary
}
I finally got it with the function compact http://php.net/manual/en/function.compact.php
Here's the code:
First I select the variables I need in my options:
'callbacks' => ['businessImage' => ['configurationData', 'configuration', 'form', 'request']]
Second I call the variables with compact, but I had to use extract here because if I didn't configurationData variable wasn't modified, which I don't understand since I had previously referenced it.
if (!empty($options[ 'callbacks' ])) {
foreach ($options[ 'callbacks' ] as $callback => $variables) {
$variables = compact($variables);
$this->$callback($variables);
extract($variables);
}
}
Third callback applied and referenced:
/**
* #param array $params
* #return array $configurationData
*/
private function businessImage(&$params)
{
extract($params,EXTR_REFS);
// more code here ......
$configurationData[ "image" ] = $originalImageName;
$configurationData[ "favicon" ] = $originalFaviconName;
$configurationData[ "login_icon" ] = $originalLoginIconName;
$configurationData[ "sidebar_icon" ] = $originalSidebarIconName;
return $configurationData;
}
This works correctly in my website, but as I said before I do not understand why I have to call back the function extract, if I have already passed it referenced in the same callback as you see in my last code.
I am trying to make a simple Panels module. I have a form with text fields were I can enter value, then that value prints to the .tpl file, via a render function, eg:
function my_module_panel_render($subtype, $conf, $args, $contexts) {
$block = new stdClass();
$block->content = [
'#theme' => 'my_tpl',
'#config' => $conf,
];
return $block;
}
Then on .tpl:
<?php print $config['name_field']; ?>
This works fine.
But I want to alter the value slightly. I've learnt I need a hook_preprocess_theme() function, which I have added.
But then how do I actually go about altering the values? How do I then return the altered value to $conf?
Doing something like
$conf['name_field'] = $conf['name_field'] . $some_other_stuff;
Doesn't seem to work.
Would anyone know what I could do?
OK, what I didn't relise was that $conf was a sub-array of the $variables array, which hook_preprocess_theme() takes as a parameter, as seen on my hook_theme:
function my_module_theme() {
return [
'my_module' => [
'template' => 'theme/my_theme',
'render element' => 'element',
'variables' => [
'config' => NULL,
],
],
];
}
So, my hook_preprocess_theme() function now looks like:
function my_module_preprocess_my_theme(&$vars) {
$conf = $vars['config'];
$vars['config']['name_field'] = $conf['name_field'] . $some_other_stuff;
}
I place this question, because I have a weird conversion of an array into boolean and I don't really know what's wrong with that.
I have checked my code very carefully, and I don't find any issue that can modify my code.
Do you see something wrong ? And if so, can you help me please ?
So, as requested I am going to explain in more details my problem.
So, I have this class, and I have a method called load_configuration() that loading some php files, returning an array of values each.
The array, returned by those files, are stored in equivalent property in the class.
Inside the load_configuration() method, I do a var_dump, for my class properties fields and settings, and I get the following result:
array (size=1)
'text' =>
array (size=3)
'type' => string 'text' (length=4)
'label' => string 'Hello world! goes here.' (length=23)
'default' => string 'Hello world!' (length=12)
Also, I have create an array at the end of the method load_configuration() and I return the arrays.
Then when I try to use the either the method properties or the returned values from the method load_configuration() I get the following with var_dump()
// In case of the returned array
array (size=2)
'fields' => boolean true
'settings' => boolean true
// In case of the class property
boolean true
But I don't see the reason. This is a very weird modification.
For better understanding look the comments in methods load_configuration() and get_template_variables()
class SS24_Widget extends \SiteOrigin_Widget {
protected $config_folder = 'Config';
protected $form_settings = 'settings';
protected $form_fields = 'fields';
protected $fields = array();
protected $settings = array();
public function __construct( $unique_widget_id = '', $widget_name = '' ) {
if ( empty( $unique_widget_id ) ) {
$unique_widget_id = uniqid( 'widget-' );
}
$this->load_configuration();
parent::__construct(
$unique_widget_id, // The unique id for your widget.
$widget_name, // The name of the widget for display purposes.
$this->settings, // The widget settings.
array(), // The $control_options array, which is passed through to WP_Widget.
$this->fields, // The widget fields.
$this->get_dir() . '/' // The $base_folder path string.
);
}
protected function load_configuration() {
$config_files = $this->get_dir() . '/' . $this->config_folder . '/*.php';
$fields = array();
$settings = array();
foreach ( glob( $config_files ) as $file ) {
switch( basename( $file, '.php' ) ) {
case $this->form_settings:
$this->settings = $this->{$this->form_settings} = require_once $file;
$settings = $this->settings;
break;
case $this->form_fields:
$this->fields = $this->{$this->form_fields} = require_once $file;
$fields = $this->fields;
break;
}
}
// This print out the following:
// array (size=1)
// 'text' =>
// array (size=3)
// 'type' => string 'text' (length=4)
// 'label' => string 'Hello world! goes here.' (length=23)
// 'default' => string 'Hello world!' (length=12)
var_dump($fields);
return array(
'fields' => $fields,
'settings' => $this->settings,
);
}
// ...
public function get_template_variables( $instance, $args ){
$c = $this->load_configuration();
// While the following printint out the following:
// array (size=2)
// 'fields' => boolean true
// 'settings' => boolean true
//
// boolean true
var_dump($c);
echo "<br />";
var_dump($this->fields);
return $variables;
}
}
Can someone please explain me what possibly it's wrong with this code ?
UPDATE #1
I found that the parent class has a protected member property named $fields so in my local code I have change now the variable into a fields_settings, but still is converted into boolean.
require_once is very similar to include_once.
If a file has already been included, they will both return boolean TRUE.
Quoting PHP documentation:
If the code from a file has already been included, it will not be included again, and include_once returns TRUE. As the name suggests, the file will be included just once.
Documentation of require_once points to include_once. This is why the block mentions the former.
To solve your problem, make sure you use require instead.
I think the problem is that you execute load_configuration() in the constructor, so the require_once statement returns the correct values here. In the next call to load_configuration(), the require_once statement returns boolean "true", indicating that the file was already included.
Use require instead of require_once.
I have this code in layout/formsde/url.phtml:
<?php
$use_url = $this->use_url;
foreach($this->match_de as $k=>$v) {
if($this->serverUrl(true) == $k) {
$use_url = $v;
}
}
?>
I have 1000 pages with following line:
<?=$this->render("layout/formsde/url");?>
Now the problem is $this->use_url and $this->match_de is null its not getting the value from Controllers where it is assigned as below:
return new ViewModel(array(
'description' => $this->de_desc,
'use_url' => $this->layout()->use_url,
'match_de' => $this->layout()->match_de,
));
How can i pass the value to ->render() ? so that i have $this->match_de value with exact which is in controller?
You can pass array with values you need to the render
//Example
$this->render('layout/formsde/url', array(
'use_url' => $this->use_url,
'match_de' => $this->match_de));
Its possible to define variables in the template which is rendered by the view renderer Zend\View\Renderer\PhpRenderer. You can pass an second argument as array.
<?=$this->render("layout/formsde/url", array(
'description' => $this->de_desc,
'use_url' => $this->layout()->use_url,
'match_de' => $this->layout()->match_de,
));?>
More information about PhpRenderer::render() method can be found in the here.
I was wondering if it was possible to determine what the current namespace was when the function was being called. I have this function declaration:
<?php
namespace Site\Action;
function add ($hook, $function) {
/**
* determine the namespace this was called from because
* __NAMESPACE__ is "site\action" :(
*/
if (is_callable($function))
call_user_func($function);
}
?>
And on another file:
<?php
namespace Foo;
function bar () {
}
?>
And let's say I have this as my procedural code:
<?php
namespace Foo;
Site\Action\add('hookname', 'bar');
?>
It would make sense to assume that Bar in this case was intended to resolve as Foo\bar since that was the namespace it was called from.
That was a long explanation so again, is it possible to determine the active namespace where Site\Action\add() was called from?
Thanks in advance.
What you are looking for is : ReflectionFunctionAbstract::getNamespaceName
If you want to know where you're coming from debug_backtrace() is your friend.
The following should solve your puzzle:
function backtrace_namespace()
{
$trace = array();
$functions = array_map(
function ($v) {
return $v['function'];
},
debug_backtrace()
);
foreach ($functions as $func) {
$f = new ReflectionFunction($func);
$trace[] = array(
'function' => $func,
'namespace' => $f->getNamespaceName()
);
}
return $trace;
}
Just call it from anywhere to see the backtrace.
I modified your "procedural" code file as follows:
namespace Foo;
function bar ()
{
var_export(backtrace_namespace());
}
/** The unasked question: We need to use the fully qualified name currently. */
function go()
{
\Site\Action\add('hookname', 'Foo\\bar');
}
go();
The Result from including this file will be the following on stdout:
array (
0 =>
array (
'function' => 'backtrace_namespace',
'namespace' => '',
),
1 =>
array (
'function' => 'Foo\\bar',
'namespace' => 'Foo',
),
2 =>
array (
'function' => 'call_user_func',
'namespace' => '',
),
3 =>
array (
'function' => 'Site\\Action\\add',
'namespace' => 'Site\\Action',
),
4 =>
array (
'function' => 'Foo\\go',
'namespace' => 'Foo',
),
)
Now for bonus points the answer to the hidden question:
How do I resolve the calling namespace to avoid using fully qualified function name as argument?
The following will allow you to call the function as you intended:
Site\Action\add('hookname', 'bar');
Without getting the dreaded:
Warning: call_user_func() expects parameter 1 to be a valid callback, function 'bar' not found or invalid function name
So before you redesign try this on for size:
namespace Site\Action;
function add($hook, $function)
{
$trace = backtrace_namespace();
$prev = (object) end($trace);
$function = "$prev->namespace\\$function";
if (is_callable($function))
call_user_func($function);
}
I see no reason why debug_backtrace should not be used, this is what it is there for.
nJoy!
If you were using closures instead of static functions you can always ask the reflection API
namespace A;
$closure = function($word = 'hello')
{
return $word;
};
...
$r = new ReflectionFunction($closure);
print $r->getNamespaceName();
i don't know if i'm missing something, but with this:
http://php.net/manual/en/reflectionclass.getnamespacename.php, i think that you can get namespace of the object you are using.