Nested functions, variables - PHP - php

function partners($atts ) {
extract(shortcode_atts(array(
'ids' => null,
'extra_options' => 'something' <----------------- in wordpress I can read this value using local $extra_options
), $atts));
global $extra_options; <----------------- trying to change local var to global
function print_partners_scripts() {
global $extra_options; <----------------- reading above variable
echo '<script type="text/javascript">' . "\n";
echo 'jQuery(document).ready( function() {'. "\n";
echo ' $(".partners-slider").bxSlider({
slideWidth: 924,
auto: 0,
autoStart: 0,
moveSlides: 1,
minSlides: 3,
maxSlides: 8,
pager: false,
controls: false,
slideMargin: 5,
' . $extra_options . ' <----------------- var is empty
});' . "\n";
echo '});' . "\n";
echo '</script>' . "\n";
}
add_action( 'wp_footer', 'print_partners_scripts' );
$ids = explode( ',', $ids );
$output = '<div class="ps-wrap"><div class="partners-slider">';
foreach($ids as $id) {
$img_attr = wp_get_attachment_image_src( $id, 'full' );
$output .= '<div class="pslide"><img src="' . $img_attr[0] . '" /></div>';
}
$output .= '</div></div>';
return $output;
}
Hi, I'm trying to read var $extra_options inside print_partners_scripts(). The variable is set in the partners() function. I've tried to make it global and simply use it in certain place but I guess I'm doing something wrong ;)
Thanks in advance !

Firstly, PHP doesn't support nested functions in the way you are trying to use them.
You can write this:
function outer() { function inner() {} }
outer();
But all that happens is that when outer(); is executed, the inner() function is declared as a normal function. So the code is exactly the same as this:
function outer() {}
function inner() {}
outer();
Secondly, variables in PHP (unless prefixed, with a class or object name) are always scoped to the current function. The global keyword imports a reference to a global variable into the current function's scope; it cannot be used to export a variable which was already defined.
It is generally best to only use the global keyword at the very beginning of a function, to import all the global variables needed by that function. Even better, do not use global variables, since they lead to "spaghetti code" which is hard to understand and debug.
If you declare the variable global before running extract, this will work, but I would strongly advise against using either feature.
function foo_with_too_much_magic()
{
// Import global variable. Very hard to track where this came from.
global $some_var;
// Let's assume this array comes from somewhere and isn't hard-coded
$some_array = array('some_var' => 'some_value');
// Export variables from an array. This is like telling PHP to write different code each time it runs, with different variable names.
extract( $some_array );
}
foo_with_too_much_magic();
var_dump($some_var);
Here is a version of the above without the discouraged features:
function foo_with_no_magic()
{
// Let's assume this array comes from somewhere and isn't hard-coded
$some_array = array('some_var' => 'some_value');
// You know which variable you want, so don't need the magic "export"
// Note that you don't have to call it $some_var
$some_var = $some_array['some_var'];
// Now you have the variable, you can manipulate it, pass it to another function, or return it
// In fact, you could also return $some_array['some_var'] directly, without the extra assignment
return $some_var;
}
// This variable name no longer needs to be the same as what was used in the foo_with_no_magic() function
$some_var = foo_with_no_magic();
var_dump($some_var);

Here is an example of putting the code into a class format which the direction I would go FYI, it may be useful to learn a bit more of the OOP practices for PHP (http://php.net/manual/en/language.oop5.php):
#1) Get the data you wish to pass into your function.
$data = "TEST";
get_partners($data);
#2) Call your function.
function get_partners($atts) {
//Extract using ($att) passed in from your call.
//The shortcode_atts function should be accessible by the file containing this function.
extract(shortcode_atts(array(
'ids' => null,
'extra_options' => 'something' //in wordpress I can read this value using local $extra_options
), $atts));
//Create a new class element that will build your data for your and allow you to pass in your variable on the fly.
$p = new partners();
$p->extra_options= $atts; //Pass the variable here.
$p->print_partners_scripts();
}
#3) Define Class here.
class partners {
var $extra_options;
public function print_partners_scripts()
{
$output = '<script type="text/javascript">' . "\n";
$output .= 'jQuery(document).ready( function() {'. "\n";
$output .= ' $(".partners-slider").bxSlider({
slideWidth: 924,
auto: 0,
autoStart: 0,
moveSlides: 1,
minSlides: 3,
maxSlides: 8,
pager: false,
controls: false,
slideMargin: 5,
' . $this->extra_options . '
});' . "\n";
$output .= '});' . "\n";
$output .= '</script>' . "\n";
$output .= $this->additional_data();
echo $output;
}
protected function additional_data()
{
add_action( 'wp_footer', 'print_partners_scripts' );
$ids; #Where is this defined?
$ids = explode( ',', $ids );
$output = '<div class="ps-wrap"><div class="partners-slider">';
foreach($ids as $id)
{
$img_attr = wp_get_attachment_image_src( $id, 'full' );
$output .= '<div class="pslide"><img src="' . $img_attr[0] . '" /></div>';
}
$output .= '</div></div>';
return $output;
}
}

Related

View a PHP Closure's Source

Is it possible to reflect into or otherwise view the source of a PHP closure object? That is, if I do something like this
$closure = function()
{
return 'Hi There';
};
and then something like this
var_dump($closure);
PHP outputs
object(Closure)[14]
That is, I know the object's a closure, but I have no idea what it does.
I'm looking for a reflection method, function, or debugging extension that will allow me to dump the actual body of anonymous function.
What you can get from PHP is limited, using reflection you can just obtain the parameter signature of the function and the start and ending line of the source code file. I've once wrote a blog article about that: http://www.metashock.de/2013/05/dump-source-code-of-closure-in-php/ ...
It lead me to the following code, using reflection:
function closure_dump(Closure $c) {
$str = 'function (';
$r = new ReflectionFunction($c);
$params = array();
foreach($r->getParameters() as $p) {
$s = '';
if($p->isArray()) {
$s .= 'array ';
} else if($p->getClass()) {
$s .= $p->getClass()->name . ' ';
}
if($p->isPassedByReference()){
$s .= '&';
}
$s .= '$' . $p->name;
if($p->isOptional()) {
$s .= ' = ' . var_export($p->getDefaultValue(), TRUE);
}
$params []= $s;
}
$str .= implode(', ', $params);
$str .= '){' . PHP_EOL;
$lines = file($r->getFileName());
for($l = $r->getStartLine(); $l < $r->getEndLine(); $l++) {
$str .= $lines[$l];
}
return $str;
}
If you have the following closure:
$f = function (Closure $a, &$b = -1, array $c = array())
use ($foo)
{
echo $this->name;
echo 'test';
};
closure_dump() will give the following results:
function (Closure $a, &$b = -1, array $c = array (
)){
use ($foo)
{
echo $this->name;
echo 'test';
};
You see it is imperfect (the array param). Also it will not handle some edge cases properly, especially if closures are nested or multiple inline closures will getting passed to a function in one line. The latter looks most problematic to me. Since, you get only the starting and ending line from reflection, both functions will be on that line in this case and you have no useful information to decide which one of them should get dumped. So far, I didn't found a solution for that, also I'm unsure if there is a solution.
However, in most cases, it should at least being helpful for debugging, as long as you don't rely on it. Feel free to enhance it!

Getting a random object from an array in PHP

First and foremost, forgive me if my language is off - I'm still learning how to both speak and write in programming languages. How I can retrieve an entire object from an array in PHP when that array has several key, value pairs?
<?php
$quotes = array();
$quotes[0] = array(
"quote" => "This is a great quote",
"attribution" => "Benjamin Franklin"
);
$quotes[1] = array(
"quote" => "This here is a really good quote",
"attribution" => "Theodore Roosevelt"
);
function get_random_quote($quote_id, $quote) {
$output = "";
$output = '<h1>' . $quote["quote"] . '.</h1>';
$output .= '<p>' . $quote["attribution"] . '</p>';
return $output;
} ?>
<?php
foreach($quotes as $quote_id => $quote) {
echo get_random_quote($quote_id, $quote);
} ?>
Using array_rand and var_dump I'm able to view the item in the browser in raw form, but I'm unable to actually figure out how to get each element to display in HTML.
$quote = $quotes;
$random_quote = array_rand($quote);
var_dump($quote[$random_quote]);
Thanks in advance for any help!
No need for that hefty function
$random=$quotes[array_rand($quotes)];
echo $random["quote"];
echo $random["attribution"];
Also, this is useless
<?php
foreach($quotes as $quote_id => $quote) {
echo get_random_quote($quote_id, $quote);
} ?>
If you have to run a loop over all the elements then why randomize hem in the first place? This is circular. You should just run the loop as many number of times as the quotes you need in output. If you however just need all the quotes but in a random order then that can simply be done in one line.
shuffle($quotes); // this will randomize your quotes order for loop
foreach($quotes as $qoute)
{
echo $quote["quote"];
echo $quote["attribution"];
}
This will also make sure that your quotes are not repeated, whereas your own solution and the other suggestions will still repeat your quotes randomly for any reasonably sized array of quotes.
A simpler version of your function would be
function get_random_quote(&$quotes)
{
$quote=$quotes[array_rand($quotes)];
return <<<HTML
<h1>{$quote["quote"]}</h1>
<p>{$quote["attribution"]}</p>
HTML;
}
function should be like this
function get_random_quote($quote_id, $quote) {
$m = 0;
$n = sizeof($quote)-1;
$i= rand($m, $n);
$output = "";
$output = '<h1>' . $quote[$i]["quote"] . '.</h1>';
$output .= '<p>' . $quote[$i]["attribution"] . '</p>';
return $output;
}
However you are not using your first parameter-$quote_id in the function. you can remove it. and call function with single parameter that is array $quote
Why don't you try this:
$quote = $quotes;
$random_quote = array_rand($quote);
$random = $quote[$random_quote];
echo '<h1>' . $random["quote"] . '.</h1><br>';
echo '<p>' . $random["attribution"] . '</p>';
Want to create a function:
echo get_random_quote($quotes);
function get_random_quote($quotes) {
$quote = $quotes;
$random_quote = array_rand($quote);
$random = $quote[$random_quote];
return '<h1>' . $random["quote"] . '.</h1><br>'.'<p>' . $random["attribution"] . '</p>';
}
First, you dont need the $quote_id in get_random_quote(), should be like this:
function get_random_quote($quote) {
$output = "";
$output = '<h1>' . $quote["quote"] . '.</h1>';
$output .= '<p>' . $quote["attribution"] . '</p>';
return $output;
}
And I cant see anything random that the function is doing. You are just iterating through the array:
foreach($quotes as $quote_id => $quote) {
echo get_random_quote( $quote);
}
According to http://php.net/manual/en/function.array-rand.php:
array_rand() Picks one or more random entries out of an array, and
returns the key (or keys) of the random entries.
So I guess $quote[$random_quote] should return your element, you can use it like:
$random_quote = array_rand($quotes);
echo get_random_quote($quote[$random_quote]);

PHP dynamically define bunch of functions

Is there a way to define few similar php function dynamically in a manner like
define('MAX_LOG_LEVEL', 10);
_log_level( $string, $level = 0 ) {
global $loglevel;
if( $level >= $loglevel ) {
file_put_contents(...);
}
}
for($level = 0; $level < MAX_LOG_LEVEL; $level++) {
function _log$level( $string ) {
_log_level( $string, $level );
}
}
that's because i have to create my own log routines and to quickly add an log information on demand.
I know the php built in create_function that do not really do what i exactly want because will be necessary to import the reference variable in the function/method that use that
I can't also use the eval() function as explained in this question since it is disabled on the final application hosting.
One way to do it is to assign an anonymous function to a variable and call it like so:
foreach (range(1, 9) as $i)
{
${'_log' . $i} = function($string) use ($i)
{
echo 'Log ' . $i . ': ' . $string;
};
}
// Outputs Log 9: Test
$_log9('Test');
Another possibility is to use eval (although you can't use it on your current application it might be possible in future environments):
foreach (range(1, 9) as $i)
{
eval('
function _log' . $i . '($string)
{
echo \'Log ' . $i . ': \' . $string;
}
');
}
// Outputs Log 9: Test
_log9('Test');
If using this latter method take note of the warning in the manual:
Caution The eval() language construct is very dangerous because it allows execution of arbitrary PHP code. Its use thus is
discouraged. If you have carefully verified that there is no other
option than to use this construct, pay special attention not to pass
any user provided data into it without properly validating it
beforehand.
However, as others have mentioned in the comments, it is perhaps best to not define a unique function for each log-level: instead define a single function with the log-level as a parameter:
function _log($i, $string)
{
echo 'Log ' . $i . ': ' . $string;
}
// Outputs Log 9: Test
_log(9, 'Test');
A further extension to this is to define the log levels to make it clearer to both yourself and to future programmers what exactly is being logged:
define('_LOG_CRITICAL_ERROR', 9);
function _log($i, $string)
{
echo 'Log ' . $i . ': ' . $string;
}
// Outputs Log 9: Test
_log(_LOG_CRITICAL_ERROR, 'Test');
Yes, it is possible; you can do this:
$function = '_log' . $level;
$function($string);
This is known as a variable function.

PHP Undefined constant error makes no sense

I truly hope I've missed something simple here, but I'm running into a strange issue using class constants in PHP. I created a simple class called Utils and added two class constants, CRYPT_SALT and LOGIN_PAGE. I referenced those from other files, and they worked. Then I added five more class constants, and they don't work. I get "Fatal error: Undefined class constant '' in /var/www/modx/test.php on line ", where is one of the new constants, and is the line where I try to use it.
Here is the Utils class:
<?php
//
// Utils.php
//
// This class is a collection of static utility functions. Since the methods are static, they should
// all be invoked with:
//
// Utils::methodName();
//
// This class also contains global constants, which are *not* kept in Config. They should be accessed with:
//
// Utils::CONSTANT;
//
// addToCSVString -- adds an incoming string to a CSV string, possibly prepending a comma and space. Returns
// addToJSONString -- adds an incoming key/value pair to a JSON string
// jsonify -- takes in a string and replaces control characters and quotes with properly
//
require_once( "logger.php" );
class Utils {
// Constants
const CRYPT_SALT = '$6$';
const LOGIN_PAGE = '/modx/';
// Session keys
const SKEY_DEBUG = 'debug';
const SKEY_LOGIN = 'login';
const SKEY_LANG = 'curLang';
const SKEY_UID = 'userID';
const SKEY_LOGGER = 'logger';
// Members
public static $debug = false;
// Methods
//
// addToCSVString -- adds an incoming string to a CSV string, possibly prepending a comma and space. Returns
// the new string
//
public static function addToCSVString( $csvString, $newVal ) {
if ( strlen( $csvString ) > 0 ) {
$csvString .= ", ";
}
return $csvString . $newVal;
}
//
// addToJSONString -- adds an incoming key/value pair to a JSON string
//
public static function addToJSONString( $jsonString, $key, $val ) {
$debug = self::$debug;
if ( $debug ) {
$logger = Logger::singleton();
$logger->log( "In Utils::addToJSONString" );
$logger->log( "\$key = [$key]", 1 );
$logger->log( "\$val = [$val]", 1 );
}
if ( strpos( $val, "{" ) === false ) {
if ( $debug ) {
$logger->log( "Utils: this is a plain value", 1 );
}
// Val is a string
$val = self::jsonify( $val );
return self::addToCSVString( $jsonString, "\"" . $key . "\" : \"" . $val . "\"" );
} else {
if ( $debug ) {
$logger->log( "this is a JSON object", 1 );
}
// Val is a JSON object
return self::addToCSVString( $jsonString, "\"" . $key . "\" : " . $val . "" );
}
}
//
// jsonify -- takes in a string and replaces control characters and quotes with properly
// escaped JSON values
//
public static function jsonify( $val ) {
$val = str_replace( '\\', '\\\\', $val ); // convert backslashes first
$val = str_replace( "\n", '\\n', $val );
$val = str_replace( "\r", '\\r', $val );
$val = str_replace( "\t", '\\t', $val );
$val = str_replace( "\v", '\\v', $val );
$val = str_replace( "\f", '\\f', $val );
$val = str_replace( "\n", '\\n', $val );
$val = str_replace( "\n", '\\n', $val );
return $val;
}
}
?>
All the member functions were written and tested before I added the class constants, they are working.
And here is test.php, a simple test page to illustrate the problem:
<h1>Test.php</h1>
<?php
// Set up autoloader
spl_autoload_extensions( '.php,.inc' );
spl_autoload_register();
// Test class constants
echo "<b>Testing Utils class constants</b></br>\n";
echo 'Utils::CRYPT_SALT = [' . Utils::CRYPT_SALT . "]<br>\n";
echo 'Utils::LOGIN_PAGE = [' . Utils::LOGIN_PAGE . "]<br>\n";
echo 'Utils::SKEY_LOGGER = [' . Utils::SKEY_LOGGER . "]<br>\n";
echo 'Utils::SKEY_DEBUG = [' . Utils::SKEY_DEBUG . "]<br>\n";
echo 'Utils::SKEY_LOGIN = [' . Utils::SKEY_LOGIN . "]<br>\n";
echo 'Utils::SKEY_LANG = [' . Utils::SKEY_LANG . "]<br>\n";
echo 'Utils::SKEY_UID = [' . Utils::SKEY_UID . "]<br>\n";
echo "</br>\n";
?>
The exact error I get from test.php is:
Fatal error: Undefined class constant 'SKEY_LOGGER' in /var/www/modx/test.php on line 15
I've tried the following to solve this:
-- Renaming the constants, including using lower case names without underscores
-- Changing the order of the declarations.
-- Changing from double to single quotes.
-- Commenting out the declarations for CRYPT_SALT and LOGIN_PAGE
-- Showing this code to my co-workers, who are all clueless
Regardless of anything I try, CRYPT_SALT and LOGIN_PAGE work, none of the other constants work. I'm afraid I'm up against some bug deep in PHP's class system. Or maybe I've just stared at this so long that I'm missing the obvious.
Yes,
The answer is I am an idiot, as usual. :)
I had a second copy of utils.php, in the main directory of the web. That earlier version (with only CRYPT_SALT and LOGIN_PAGE defined) was the one the autoloader was finding first.
#Paolo Bergantino and #David, you were quite right to suggest making sure I was including the file I thought I was. #hakre, thanks for the get_included_files tip.
I tried your code in my system.
if you remove "require_once( "logger.php" );" into Utils.php, its working fine.
so, i thing problem in your lonnger.php file.
try it.

Check if a field/propertry exists in a variable of type object

I am using Zend_Search_Lucene, to index my website. My site indexes are not entirely similar. Some have, few fields, and some have many fields. I am trying to create a similar index through different types of table, that's why i am encountering this kind of error.
Now, when I display the result. I call some fields, which are not present in all the result which generates the error. i tried to check it with isset but it seems it totally skips the row.
foreach ($hits as $hit) {
$content .= '<div class="searchResult">';
$content .= '<h2>';
$title = array();
if(isset($hit -> name)) $title[] = $hit -> name;
if(isset($hit -> title)) $title[] = $hit -> title;
// This is the part where i get fatal error.
$content .= implode(" » ",$title);
$content .= '</h2>';
$content .= '<p>'.$this->content.'</p>';
$content .= '</div>';
}
How to check if there is anything such as $hit -> name is present in $hit
The problem you are experiencing is very very specific and has to do with the Zend_Lucene_Search implementation, not the field/property exist check in general.
In your loop, $hit is an object of class Zend_Search_Lucene_Search_QueryHit. When you write the expression $hit->name, the object calls the magic __get function to give you a "virtual property" named name. It is this magic function that throws the exception if the value to be supplied does not exist.
Normally, when a class implements __get as a convenience it should also implement __isset as a convenience (otherwise you cannot really use isset on such virtual properties, as you have found out the hard way). Since this particular class does not implement __isset as IMHO it should, you will never be able to get the name "property" blindly without triggering an exception if the relevant data does not exist.
property_exists and all other forms of reflection will also not help since we are not talking about a real property here.
The proper way to solve this is a little roundabout:
$title = array();
$names = $hit->getDocument()->getFieldNames();
if(in_array('name', $names)) $title[] = $hit -> name;
if(in_array('title',$names)) $title[] = $hit -> title;
All said, I 'd consider this a bug in ZF and probably file a report, asking for the __isset magic method to be implemented appropriately on the types it should be.
You can try property_exists.
foreach ($hits as $hit) {
$content .= '<div class="searchResult">';
$content .= '<h2>';
$title = array();
if(property_exists($hit, 'name')) $title[] = $hit -> name;
if(property_exists($hit, 'title')) $title[] = $hit -> title;
// This is the part where i get fatal error.
$content .= implode(" » ",$title);
$content .= '</h2>';
$content .= '<p>'.$this->content.'</p>';
$content .= '</div>';
}
You can also use reflection to query what fields the object has and build your content in a more programmatic way. this is good if you have a ton of fields.
$reflector = new ReflectionClass( get_class( $hit ) );
foreach( $reflector->getProperties() as $property ) {
if( in_array( $property->getName(), $SEARCH_FIELDS )
$title[] = $property->getValue( $hit );
}
more info here : http://php.net/manual/en/book.reflection.php
# Verify if exists
$hits = $index->find('id_field:100');
if (isset($hits[0])) { echo 'true'; } else { echo 'false'; }
isset will work if you simply convert your object to an array first.
<?php
class Obj {
public function GetArr() {
return (array)$this;
}
static public function GetObj() {
$obj = new Obj();
$obj->{'a'} = 1;
return $obj;
}
}
$o = \Obj::GetObj();
$a = $o->GetArr();
echo 'is_array: ' . (\is_array($a) ? 1 : 0) . '<br />';
if (\is_array($a)) {
echo '<pre>' . \print_r($a, \TRUE) . '</pre><br />';
}
echo '<pre>' . \serialize($o) . '</pre>';
?>

Categories