I have Logger-Class which is logging everything. Objects will be logged with print_r to a human-readable state. My Problem is that I have a big MVC-Object. Everytime an Exception or Error occurs, the MVC-Object will also be printed in the Log by print_r. This results in a very very long Logfile that is not really friendly to read.
I tried to set a __toString() method to my MVC-Class but this don't work. I also get the complete MVC-Object in Log. The MVC is a Singleton and is referenced on every Object. So to simple exclude the Object before it cames into the print_r is not that easy.
Is there any way to exclude Objects from print_r?
My Methods:
LOG-Class errorHandler-Method:
public static function errorHandler($errno, $errstr, $errfile, $errline, $vars) {
//If # is set, don't do anything!
if(error_reporting() === 0) {
return;
}
//Get StackTrace with low memory usage ;)
$e = new Exception();
$stackStr = $e->getTraceAsString();
//Build ErrorMessage for Log
$message = 'File: '.$errfile.' - L: '.$errline."\n".
'Code: '.$errno."\n".
'Message: '.$errstr."\n".
'Vars: '.print_r($vars, true)."\n".
'Stacktrace: '.$stackStr;
self::error($message);
}
LOG-Class exceptionHandler-Method:
public static function exceptionHandler(Exception $e) {
$message = get_class($e).': '.$e->getMessage()."\n".
'File: '.$e->getFile().' - L: '.$e->getLine()."\n".
'Code: '.$e->getCode()."\n".
'Message: '.$e->getMessage()."\n".
'Stacktrace: '.$e->getTraceAsString();
self::error($message);
}
LOG-Class error-Method:
public static function error($data, $file='system.log') {
$config = Megaira_PropertyConfiguration::getInstance();
switch($config->get('LOG_MODE')) {
case'DEEPDEBUG':
case'ERROR':
case'WARNING':
case'INFO':
case'DEBUG':
self::writeToLog('ERROR', $data, $file);
break;
}
}
LOG-Class writeToLog-Method:
private static function writeToLog($mode='', $text='', $file=''){
if(!is_string($text) && !is_numeric($text)) {
$text = print_r($text, true);
}
$config = Megaira_PropertyConfiguration::getInstance();
if(!$config->get('LOGGINGACTIVE')) { return; }
self::writeLineToFile($mode, $text, $file);
}
Setup the Error- and Exception-handler:
//Set Error and Exception Handler
set_error_handler(array(new LOG(), 'errorHandler'));
set_exception_handler(array(new LOG(), 'exceptionHandler'));
Thanks
Some Testing:
public static function print_r_filtered($object, $ret=false) {
$filtered = array(
'Megaira_MVC'
);
$text = print_r($object, true);
foreach($filtered as $filter) {
$search = '#('.$filter.'\sObject)\n(\s+)\).*?\n\2\)\n#s';
$replace = "$1";
$text = preg_replace($search, $replace, $text);
}
if($ret)
return $text;
echo $text;
}
Did not work. Maybe RegEx fail?
Solution:
It was a Design flaw. The errorHandler is Logging all Objects that are used on the place the error occurs. So in the index.php are the following code:
$mvc = Megaira_MVC::getInstance();
So this peace of code produced a logging of the Var $mvc with print_r through the errorHandler in LOG-Class.
Conclusion for me: Don't use Variables on big Singleton-Objects or use unset() if the Var is not needed anymore.
__toString() is called, when the object is casted to a string. You may try something like
$objectString = method_exists($object, '__toString')
? (string) $object
: print_r($object, true);
Use is_object() to find out, if a value is an object or not.
At all
$string = !is_object($value) || method_exists($value, '__toString')
? (string) $value
: print_r($value, true);
You could wrap print_r with your own function that checks whether the provided data includes any objects, using the is_object() function. Similarly, you could use is_a() if you only want to exclude certain classes of objects.
as a html solution you can use:
<pre><?=print_r(log_content);?></pre>
to display your log file better. I am displaying the log files in this way.
if (is_object( $foo ))
{
print_r( $foo->__toString() );
} else {
print_r( $foo );
}
If you can alter the Logger class, you can check for the class befpre you print it:
if(!($var instanceof HeavyMVCObject)){
print_r($var);
}
Related
For testing/debugging/evaluation purposes I want to write wrappers for selected entries of the PHP autoloader chain. As autoloader methods are restricted to a single argument with a given value (the class name) the only way to tell the wrapper what to do seems to be the method name. I thus iterate over spl_autoload_functions(), replace the selected items with dynamically created calls of methods like Wrapper::handler_xxxx and save the original information in that class. Wrapper has a method public static function __callStatic() which should evaluate the incoming requests, do its testing/debugging/evaluating work and otherwise redirect to the (saved) original handler. However, this does not work but leads to errors like this one:
PHP Fatal error: Uncaught LogicException: Passed array does not specify an existing method (class 'Wrapper' does not have a method 'autoload_mdGVzdG1ldGhvZAo') in [somewhere]
Seems that the autoloader needs real methods and does not work with PHP-magic. Whether this is a bug or a feature (if the latter: why?), is there any way to work around it?
Edit: some code example upon request, I am not sure if this helps understanding the problem; this is by far not finished, but just an attempt to get the basic idea up and running. Calling the magic method manually (by the name given above) works fine - it is just that the autoloader-logic is not willing to call it.
class Wrapper {
const AUTOLOAD_PREFIX = 'autoload';
public static function __callStatic(String $name, Array $arguments) {
if (strpos($name, self::AUTOLOAD_PREFIX) === 0) {
try {
# valid signature found
if (preg_match(sprintf('/^%s(_c([^_]+))?_m([^_]+)$/', self::AUTOLOAD_PREFIX), $name, $matches)) {
# call via class/method
$method = self::decodeMethodName($matches[3]);
if ($matches[2]) {
$class = self::decodeMethodName($matches[2]);
$class::$method($arguments);
}
else {
# call global function
$method($arguments);
}
}
else {
# invalid name
throw new \Exception(sprintf('Invalid static call of %s::%s', __CLASS__, $name));
}
}
catch (\Throwable $e) {
echo "I got you.";
die;
}
}
else {
throw new \Exception(sprintf('Invalid static call of %s::%s', __CLASS__, $name));
}
return;
}
private static function encodeMethodName(String $method) : String {
# protect and encode method names
return str_replace(array('+', '/', '='), array(0x81, 0x82, ''), base64_encode($method));
}
private static function decodeMethodName(String $method) : String {
# reconstruct method names
return base64_decode(str_replace(array(0x80, 0x81), array('+', '/'), $method));
}
public function protectAutoloader(?String $originalClass, String $originalMethod) : Void {
$stack = array();
$autoLoaders = spl_autoload_functions();
while (!$done && count($autoLoaders) > 0) {
$item = array_pop($autoLoaders);
if (
is_string($item) &&
is_null($originalClass) &&
($originalMethod === $item)
) {
# global function
$replacement = array(
__CLASS__,
sprintf('%s_m%s', self::AUTOLOAD_PREFIX, self::encodeMethodName($item)),
);
$done = true;
}
elseif (
is_array($item) &&
($item[0] === $originalClass) &&
($item[1] === $originalMethod)
) {
# static method
$replacement = array(
__CLASS__,
sprintf('%s_c%s_m%s', self::AUTOLOAD_PREFIX, self::encodeMethodName($item[0]), self::encodeMethodName($item[1])),
);
$done = true;
}
else {
# don't touch anything else (closures)
$replacement = $item;
}
# remove item and push to the stack
spl_autoload_unregister($item);
array_push($stack, $replacement);
}
# restore autoloader chain
while (count($stack) > 0) {
$item = array_pop($stack);
spl_autoload_register($item, true);
}
return;
}
I'd activate this for a specific autoloader:
$wrapper->protectAutoloader(NULL, 'testmethod');
Now testmethod() (if existing in the chain) will be replaced by Wrapper::autoload_mdGVzdG1ldGhvZAo() and if e.g. a syntactically broken file were autoloaded the message "I got you" would be printed.
I have a function
function my_dump($a,$name){
echo '<pre>'.$name.":\n";
var_export($a);
echo '</pre>';
}
How can I achieve this with only one argument so the function recognizes the name of the called variable itself?
for example:
my_dump($cool_variable_name);
will output:
Name: "cool_variable_name"
Content: array(...
If you need to debug code, then using tools like xdebug is a lot more flexible and efficient than homebrew variable dumps; but debug_backtrace() (although a big overhead) might give you what you need. Extract the filename and line number of the call to your debug dump function, and parse that line to extract the variable name that's used when calling the function
function my_dump($a) {
$backtrace = debug_backtrace()[0];
$fh = fopen($backtrace['file'], 'r');
$line = 0;
while (++$line <= $backtrace['line']) {
$code = fgets($fh);
}
fclose($fh);
preg_match('/' . __FUNCTION__ . '\s*\((.*)\)\s*;/u', $code, $name);
echo '<pre>'.trim($name[1]).":\n";
var_export($a);
echo '</pre>';
}
$foo = 'bar';
$baz = array(
'Hello',
'World'
);
my_dump($foo);
my_dump(
$baz
);
If your PHP version doesn't support array dereferencing, change
$backtrace = debug_backtrace()[0];
to
$backtrace = debug_backtrace();
$backtrace = $backtrace[0];
If your call to my_dump() spans multiple lines (like my $baz example), you'll need a slightly more sophisticated parser to extract the variable name from your code.
function my_dump($a) {
$backtrace = debug_backtrace()[0];
$fh = fopen($backtrace['file'], 'r');
$line = 0;
while (++$line <= $backtrace['line']) {
$code = fgets($fh);
}
fclose($fh);
preg_match('/' . __FUNCTION__ . '\s*\((.*)\)\s*;/u', $code, $name);
echo '<pre>'.trim($name[1]).":\n";
var_export($a);
echo '</pre>';
}
It does not run right - because used expression does not give correct result.
Using of \s* has not sense because nothing can be between name of function and brackets with parameters.
Using of ; in expression has not sense too. At least because is allows only standalone using of this function where result of this function is given to else variable. But sometimes it may be needed to use this function as parameter.
And using of (.*) will allow also all that is behind variable name given as parameter - even if there is semicolon.
So, it is needed to exclude of sign of end bracket, ), from result. There are many ways how to do that.
You can use form from code of function below or this (or something else)
'/'.__FUNCTION__.'\((\w+)\)/'
Whole function may be very simplified into (for example, this is case of class function):
protected function Get_VariableNameAsText($Variable="")
{
$File = file(debug_backtrace()[0]['file']);
for($Line = 1; $Line < count($File); $Line++)
{
if($Line == debug_backtrace()[0]['line']-1)
{
preg_match('/'.__FUNCTION__.'\(([^\)]*)\)/', $File[$Line], $VariableName);
return trim($VariableName[1]);
}
}
}
I deleted using of var_export because I don't see why to use it, when I only want to get variable name as string (text). And printing of given string is better outside of this function.
If you have not 5.4 or greater, you need to change code
$File = file(debug_backtrace()[0]['file']);
if($Line == debug_backtrace()[0]['line']-1)
on
$Trace = debug_backtrace();
$File = file($Trace[0]['file']);
if($Line == $Trace[0]['line']-1)
... I beg your pardon for new answer instead change of previous, but it would be too long (and thus uncomfortable) for reading.
Below is function from previous answer, that is improved for multiple usage of this function. This version is capable to get name of correct variable in case of multiple usage in one line.
It is not capable to get name of more than one variable - at least because I don't think it would be good (useful for anything).
Differences:
adding of integer parameter $Index
new function for regular expression
new regular expression
new handling of result of regular expression
parameter $Index is not demanded - but its ignoring whenever else than the first usage of that function will give wrong result
\x20 in expression means space
exception class and exception message in try-catch block may be rewritten as you need
protected status of function may be changed or deleted
protected function Get_VariableNameAsText($Variable="", $Index="")
{
$File = file(debug_backtrace()[0]['file']);
try
{
if(!empty($Index) && !is_integer($Index))
{
throw new UniT_Exception(UniT_Exception::UNIT_EXCEPTIONS_MAIN_PARAM, UniT_Exception::UNIT_EXCEPTIONS_PARAM_VALTYPE);
}
}
catch(UniT_Exception $Exception)
{
$Exception -> ExceptionWarning(__CLASS__, __FUNCTION__, $this -> Get_Parameters(__CLASS__, __FUNCTION__)[1], gettype($Index), 'integer');
}
for($Line = 1; $Line < count($File); $Line++)
{
if($Line == debug_backtrace()[0]['line']-1)
{
preg_match_all('/'.__FUNCTION__.'\((?<type>[a-z]{1,}\:{2}\${1}|\$this\x20{0,1}\-\>{1}\x20{0,1}|\${1})(?<variable>[A-Za-z0-9_]{1,})\x20{0,1}\,{0,1}\x20{0,1}(?<index>[0-9]{0,})\x20{0,}\)/', $File[$Line], $VariableName, PREG_SET_ORDER);
if(empty($Index))
{
return $VariableName[0]['type'].$VariableName[0]['variable'];
}
else
{
return $VariableName[$Index]['type'].$VariableName[$Index]['variable'];
}
}
}
}
I hope it will help you, like or better than previous version.
Edit: New expression allows using of more types of variables (not only common).
If it is a simple script, where all variables are defined in the global scope, you could get for the $GLOBALS solution:
function my_dump($a){
if(is_string($a) and isset($GLOBALS[$a])) {
echo "varname: $a\n";
$a=&$GLOBALS[$a];
}
echo '<pre>'.$name.":\n";
var_export($a);
echo '</pre>';
}
this way you can call the dump function with the variable name instead
my_dump("cool_variable_name");
instead of
my_dump($cool_variable_name);
There is no way to do this.
When the function is called the value of the parameter gets pasted into your $a , so every attempt to get the name of it would return "a".
http://us3.php.net/get_defined_vars will fail, because it only returns variables inside your function (that means "a").
You may use an array with one item, but I guess that's not what you intend to do.
I am trying to create an form validator class that will check to make sure that people have typed a particular number of characters into specified fields. Here is my page:
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
require_once('functions.php');
require_once('formValidator.php');
$validator = new FormValidator();
$validator->setFirst($_POST['first']);
$validator->setLast($_POST['last']);
$validator->setEmail($_POST['email']);
$errors = array();
$validator->checkLength($errors);
echo $errors;
if (!empty($errors)) {
echo '<p>' . $message . '</p>';
}
}
// form here (code above is in PHP tags, of course)
Here is my class:
class FormValidator {
public $first;
public $last;
public $email;
public $fields_with_lengths;
public function setFirst($first) {
$this->first = $first;
}
public function setLast($last) {
$this->last = $last;
}
public function setEmail($email) {
$this->email = $email;
}
function setLengths() {
$this->fields_with_lengths = array('first' => 2, 'last' => 2, 'email' => 8);
}
function checkLength($fields_with_lengths) {
$length_errors = array();
foreach($fields_with_lengths as $fieldname => $maxlength) {
if (strlen(trim($_POST[$fieldname])) > $maxlength) {
$length_errors[] = $fieldname;
}
}
if (!empty($length_errors)) {
$message = 'There were no errors';
} else {
$message = 'There were errors';
}
return $message;
}
}
$validator = new FormValidator();
$validator->setFirst($_POST['first']);
$validator->setLast($_POST['last']);
$validator->setEmail($_POST['email']);
Can anyone see what I am doing wrong? I'd be grateful for some help. Thanks.
You're passing an empty array into checkLength(), then iterating over it (so the for loop will never have anything to iterate over). You're returning $message from checkLength() but not assigning it to anything.
$errors = array('first' => 8,
'last' => 8,
'email' => 16
);
$message = $validator->checkLength($errors);
In PHP by default all values to functions are passed as values, not references (unless they are objects).
First of all, if you want to use a variable as a return value, you'd have to define checkLength($var) as:
function checkLength(&$fields_with_lengths) { /* ... */ }
You could in PHP4 do a call like checkLength(&$errors), but that's deprecated as of PHP5 and will throw an E_STRICT warning. Of course, you still can (but that functionality may be thrown away soon), though the "correct" way is, as said, giving it as reference.
An even more elegant way of acting, and what should really be done in PHP, is to return the $fields_with_lengths. This way:
function checkLength() {
$var = array();
// Your code
return $var;
}
And in the call to the function, just say
$return = $validator->checkLength();
But that does not solve the logic of your problem.
You are iterating over a parameter which is an empty array.
You are not iterating over $validator->fields_with_lengths.
Even more, $fields_with_lengths, in your code is not initialized ever.
You would have to call $validator->setLengths().
A better way of doing is (you are using PHP5) declaring a constructor, which in PHP5 is done with the magic function __construct().
However, as you are just initializing the variable always with the same constant values, you can do that in the very definition, just like this:
So you would have something like:
class FormValidator {
pubilc $first;
pubilc $last;
pubilc $email;
pubilc $fields_with_lengths = array('first' => 2, 'last' => 2, 'email' => 8);
// Rest of code
}
What's more..
¿Why are the attributes marked as public?
OOP theory tells us to close the scope of visibility as much as possible. And as you have setters, you don't need to access the attributes directly.
You can in PHP have a magic__get($var)to return a private $attribute if you want the syntactic sugar ofecho $validator->first;`.
Example:
function __get($var) {
if(isset($this->{$var})) return $this->{$var}; // this $this->{$var} could even be written as $this->$var but I did it like that for clarity.
}
If you also want the syntactic sugar of $validator->first = "John"; you can do the same with the setter, as in:
function __set($var, $value) {
if(isset($this->{$var})) {
return $this->{$var} = $value;
} return null; // Or you can throw an exception if you like
}
In PHP, as opposed to other languages, such as Java, you must explicitly use $this as the current instance of an object within a function.
So your checkLength is misdefined.
You should also do something with the return value, instead of just dropping it.
With just that minor modifications, you could fix your code with
function checkLength() {
$length_errors = array();
foreach($this->fields_with_lengths as $fieldname => $maxlength) {
if (strlen(trim($_POST[$fieldname])) > $maxlength) {
$length_errors[] = $fieldname;
}
}
if (!empty($length_errors)) {
$message = 'There were no errors';
} else {
$message = 'There were errors';
}
return array($length_errors, $message);
}
You were using a variable ($length_errors otherwise inaccessible! so you were computing data which would never be useful).
And in the call to the function, something like
$validator = new FormValidator();
// You could leave the setXXX($_POST[xxx])
$validator->first = $_POST['first'];
$validator->last = $_POST['last'];
$validator->email = ($_POST['email'];
$errors = $validator->checkLength();
echo "<p> {$errors[1]} </p>";
if(!empty($errors[0])) {
// You could do something like foreach($errors[0] as $k => $fieldname) { echo "Error in $fieldname<br />" }
}
$errors is empty because you initialize it but never change it. $message is unset because you never assign anything to it.
You're defining $errors = array();, making the errors an empty array. Nowhere do you attempt to set it to any other value, thus it remains an empty array.
I'm sure there's a very easy explanation for this. What is the difference between this:
function barber($type){
echo "You wanted a $type haircut, no problem\n";
}
call_user_func('barber', "mushroom");
call_user_func('barber', "shave");
... and this (and what are the benefits?):
function barber($type){
echo "You wanted a $type haircut, no problem\n";
}
barber('mushroom');
barber('shave');
Always use the actual function name when you know it.
call_user_func is for calling functions whose name you don't know ahead of time but it is much less efficient since the program has to lookup the function at runtime.
Although you can call variable function names this way:
function printIt($str) { print($str); }
$funcname = 'printIt';
$funcname('Hello world!');
there are cases where you don't know how many arguments you're passing. Consider the following:
function someFunc() {
$args = func_get_args();
// do something
}
call_user_func_array('someFunc',array('one','two','three'));
It's also handy for calling static and object methods, respectively:
call_user_func(array('someClass','someFunc'),$arg);
call_user_func(array($myObj,'someFunc'),$arg);
the call_user_func option is there so you can do things like:
$dynamicFunctionName = "barber";
call_user_func($dynamicFunctionName, 'mushroom');
where the dynamicFunctionName string could be more exciting and generated at run-time. You shouldn't use call_user_func unless you have to, because it is slower.
With PHP 7 you can use the nicer variable-function syntax everywhere. It works with static/instance functions, and it can take an array of parameters. More info at https://trowski.com/2015/06/20/php-callable-paradox
$ret = $callable(...$params);
I imagine it is useful for calling a function that you don't know the name of in advance...
Something like:
switch($value):
{
case 7:
$func = 'run';
break;
default:
$func = 'stop';
break;
}
call_user_func($func, 'stuff');
There are no benefits to call it like that, the word user mean it is for multiple user, it is useful to create modification without editing in core engine.
it used by wordpress to call user function in plugins
<?php
/* main.php */
require("core.php");
require("my_plugin.php");
the_content(); // "Hello I live in Tasikmalaya"
...
<?php
/* core.php */
$listFunc = array();
$content = "Hello I live in ###";
function add_filter($fName, $funct)
{
global $listFunc;
$listFunc[$fName] = $funct;
}
function apply_filter($funct, $content)
{
global $listFunc;
foreach ($listFunc as $key => $value)
{
if ($key == $funct and is_callable($listFunc[$key]))
{
$content = call_user_func($listFunc[$key], $content);
}
}
echo $content;
}
function the_content()
{
global $content;
$content = apply_filter('the_content', $content);
echo $content;
}
....
<?php
/* my_plugin.php */
function changeMyLocation($content){
return str_replace('###', 'Tasikmalaya', $content);
}
add_filter('the_content', 'changeMyLocation');
in your first example you're using function name which is a string. it might come from outside or be determined on the fly. that is, you don't know what function will need to be run at the moment of the code creation.
When using namespaces, call_user_func() is the only way to run a function you don't know the name of beforehand, for example:
$function = '\Utilities\SearchTools::getCurrency';
call_user_func($function,'USA');
If all your functions were in the same namespace, then it wouldn't be such an issue, as you could use something like this:
$function = 'getCurrency';
$function('USA');
Edit:
Following #Jannis saying that I'm wrong I did a little more testing, and wasn't having much luck:
<?php
namespace Foo {
class Bar {
public static function getBar() {
return 'Bar';
}
}
echo "<h1>Bar: ".\Foo\Bar::getBar()."</h1>";
// outputs 'Bar: Bar'
$function = '\Foo\Bar::getBar';
echo "<h1>Bar: ".$function()."</h1>";
// outputs 'Fatal error: Call to undefined function \Foo\Bar::getBar()'
$function = '\Foo\Bar\getBar';
echo "<h1>Bar: ".$function()."</h1>";
// outputs 'Fatal error: Call to undefined function \foo\Bar\getBar()'
}
You can see the output results here: https://3v4l.org/iBERh it seems the second method works for PHP 7 onwards, but not PHP 5.6.
Have a series of functions I am calling dynamically sort of like this:
$function = 'someFunction';
$x = $function();
.. however, if the function expects parameters, and I don't code it in the call, I seem to crash the page. For example:
function someFunction( $in_param1 ) {
return "SUCCESS";
}
$function = 'someFunction';
// this next line does not work
$x = $function() or die("error");
How can I handle this kind of error?
Thanks -
You can catch this using the set_error_handler function:
function handle_errors( $errno, $errstr )
{
die( "Your error will be caught here" );
}
set_error_handler( "handle_errors" );
$function = 'someFunction';
$x = $function();
Suppress? Yes. Catch? See don.neufeld's answer.
$x = #$function();
According to this page, you can enable a specific INI setting so that you can read errors suppressed in this manner.
You could redefine your function with default values for the arguments required like
function someFunction( $in_param1=null ) {
return "SUCCESS";
}
I don't know about suppressing, but you can get more information about the functions with the ReflectFunction class.
<?php
function foo($a, $b, $c) {}
$func = new ReflectionFunction('foo');
echo $func->getNumberOfParameters();
This outputs "3".