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.
Related
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();
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!
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.
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;
}
}
I found the following online but I'm having trouble implementing it
(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,#?^=%&:/~\+#]*[\w\-\#?^=%&/~\+#])?
This is what I want the php to do:
Take the following : Look here: http://www.rocketlanguages.com/spanish/resources/pronunciation_spanish_accents.php
And turn it into: Look here: http://www.rocketlanguages.com/span...anish_accents.php
If the URL is long then the a text gets broken down with a ... in the middle
Try this:
// URL regex from here:
// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
define( 'URL_REGEX', <<<'_END'
~(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))~
_END
);
// PHP 5.3 or higher, can use closures (anonymous functions)
function replace_urls_with_anchor_tags( $string,
$length = 50,
$elision_string = '...' ) {
$replace_function = function( $matches ) use ( $length, $elision_string) {
$matched_url = $matches[ 0 ];
return '<a href="' . $matched_url . '">' .
abbreviated_url( $matched_url, $length, $elision_string ) .
'</a>';
};
return preg_replace_callback(
URL_REGEX,
$replace_function,
$string
);
}
function abbreviated_url( $url, $length = 50, $elision_string = '...' ) {
if ( strlen( $url ) <= $length ) {
return $url;
}
$width_either_side = (int) ( ( $length - strlen( $elision_string ) ) / 2 );
$left = substr( $url, 0, $width_either_side );
$right = substr( $url, strlen( $url ) - $width_either_side );
return $left . $elision_string . $right;
}
(The backtick in the URL_REGEX definition confuses stackoverflow.com's syntax highlighting, but it's nothing to be concerned about)
The function replace_urls_with_anchor_tags takes a string and changes all the URLs matched within to anchor tags, shortening long URLs by eliding with ellipses. The function takes optional length and elision_string arguments in case you wish to play around with the length and change the ellipses to something else.
Here's a usage example:
// Test it out
$test = <<<_END
Look here:
http://www.rocketlanguages.com/spanish/resources/pronunciation_spanish_accents.php
And here:
http://stackoverflow.com/questions/12385770/implementing-web-address-regular-expression
_END;
echo replace_urls_with_anchor_tags( $test, 50, '...' );
// OUTPUT:
// Look here:
// http://www.rocketlangua...ion_spanish_accents.php
//
// And here:
// http://stackoverflow.co...ress-regular-expression
Note that if you are using PHP 5.2 or lower you must rewrite replace_urls_with_anchor_tags to use create_function instead of closures. Closures were not introduced until PHP 5.3:
// No closures in PHP 5.2, must use create_function()
function replace_urls_with_anchor_tags( $string,
$length = 50,
$elision_string = '...' ) {
$replace_function = create_function(
'$matches',
'return "<a href=\"$matches[0]\">" .
abbreviated_url( $matches[ 0 ], ' .
$length . ', ' .
'"' . $elision_string . '"' .
') . "</a>";'
);
return preg_replace_callback(
URL_REGEX,
$replace_function,
$string
);
}
Note that I replaced the URL regex you had found with one linked to on the page DaveRandom referred to in his comment. It's more complete, and in fact there is actually a mistake in the regex you were using -- a couple of '/' characters are not escaped (in here: [\w\-\.,#?^=%&:/~\+#]*[\w\-\#?^=%&/~\+#]). Also, it doesn't detect port numbers like 80 or 8080.
Hope this helps.
I am using this Regular expression and it is working fine for me, try this if you want
(http|https|ftp):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?