PHP dynamically define bunch of functions - php

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.

Related

Support negative values

I'm in a situation where the CSV file is getting rid of the leading zero before my import and I need to be able to account for that. Let's say that I have my value as the following:
-.0982739 -> I would want all case scenarios where it's -. to turn into -0. - Here are my attempts:
if (str_contains($this->longitude, '-.')) {
$this->longitude = '0' . $this->longitude;
};
Outputs: 00-.0989070
if ($this->longitude[0] == '.') {
$this->longitude = '0' . $this->longitude;
}
Outputs: -.0989070
To simplify things, basically any . that has nothing before it, add in a 0, otherwise use the value given.
I will need it for both longitude and latitude.
/^([-+])?\./
The above regex matches the signs - and + if they are present and immediately followed by a .. Now, capture the matched group 1 in the regex which is ([-+])? and append 0. followed by all digits after . by taking substr of the current string.
<?php
$a = ".0982739";
if(preg_match('/^([-+])?\./',$a, $matches) === 1){
$prefix = $matches[1] ?? '';
$a = $prefix . '0.' . substr($a,$prefix == '' ? 1 : 2);
}
echo $a;
Try this :
<?php
$x = -.54321 ;
echo $x . "\r\n" ;
$x = "-.12345" ;
echo $x . "\r\n" ;
echo floatval($x) . "\r\n" ;
echo sprintf("%0.5f", $x) ;
?>
I assume your CSV is returning only string values, because as soon as I echo a "native" float, the echo is just fine.
As soon as your float is correctly formatted, you can catch it in a string value if needed.
You could use str_replace:
$this->longitude = ltrim($this->longitude, '+');// remove leading +
if ($this->longitude[0]=='.' || substr($this->longitude, 0, 2)=='-.')) {
$this->longitude = str_replace('.', '0.', $this->longitude);
}
The if condition matches any string that begins with '.' or '-.' And if so, str_replace replaces the . with 0.
`$var = 0.0989070;
$neg = -$var; // easiest
$neg = -1 * $var; // bit more explicit
$neg = 0 - $var; // another version`
You could check for numbers before dot....
<?php
if (str_contains($this->longitude, '.')) {
$beforedot = strstr($this->longitude,'.',true); //Get anything before
if (is_numeric($beforedot)) { echo "do nothing"; } //check if is number do nothing
else {
$this->longitude = str_replace('.', '0.', $this->longitude); //else do replace . with 0.
}
};
?>
I created a class and assigned -.0982739 to a property called $longitude. In the constructor I did echo $this->longitude and it came out as -0.0982739 which I believe is exactly as you want it to be. I couldn't manage to reproduce it.
<?php
class Test
{
private $longitude = -.0982739;
public function __construct()
{
}
public function test()
{
echo $this->longitude;
}
}
<?php
include "Test.php";
$test = new Test();
$test->test();

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!

Store the return value from an anonymous function into a variable outside its scope

$count = 2;
$amt = 4;
$str = function($count, $amt) {
return "There is ". $count . " and " . $amt;
};
echo $str . "!";
How do I store the return value from the anonymous function into a variable? I know that the entire function itself is being stored into $str which is why this doesn't work, but is there a way to?
You should simply call $str as a function:
echo $str() . "!"
Documentation for anonymous functions as php.net: http://www.php.net/manual/en/functions.anonymous.php

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.

PHP force reset counter to 0

I have a counter of menu items, that basically add incremental value as a class to a menu system:
<?php
if ($element['#original_link']['depth'] == 1) {
static $counter = 0;
$counter++;
$class = 'count-' . $counter;
}
$output = 'some output code build';
return '<li class="' . $class . '">' .$output .'</li>';
?>
Note, the code is inside each menu item (outside a loop or array). The code will simply output the lists of unordered list, without UL:
<li class="count-1">One</li>
<li class="count-2">Two</li>, ...etc.
This works fine until I change the source of menus.
1). One is using the system menu of my CMS,
2). the latter is using a block output of that system menu,
and both output similar construct of menus, except for the fact that the latter continues the counter from the #1 rather than a reset from a 1 (although #1 is not activated once #2 takes over the place). I haven't figured out why, but it seems that #2 is a continuance of #1. While I expect each should start an increment from 1.
I can not use a reset here. Is there anything obvious I missed that PHP can handle here, apart from the the way of my CMS do to the counters?
Any pointer would be very much appreciated. Thanks
UPDATE, actual codes in use:
function mytheme_menu_link(array $variables) {
//dpm($variables);
$element = $variables['element'];
$sub_menu = '';
if ($element['#below']) {
$sub_menu = drupal_render($element['#below']);
}
if ($element['#original_link']['menu_name'] == variable_get('menu_main_links_source', 'main-menu')) {
if ($element['#original_link']['depth'] == 1) {
static $counter = 0;
$counter++;
$element['#attributes']['class'][] = 'count-' . $counter;
}
}
$output = l($element['#title'], $element['#href'], $element['#localized_options']);
return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
}
/**
* Implements theme_menu_tree()
No helpful variables, except for a flattened render of tree
*/
function mytheme_menu_tree__main_menu($variables) {
return '<ul class="menu">' . $variables['tree'] . '</ul>';
}
When using static variables, the values of the variable will not be reset between subsequent executions of the same code. That's the express purpose of static. Get rid of it if you do not want that behavior.

Categories