Add detail to Codeigniter Logs - php

All the errors I'm interested in debugging in Codeigniter's log files are reporting that they come from /system/core/Loader.php when they don't. For example, here's a line:
ERROR | 2014-09-22 22:35:43 | "Severity: Notice --> Undefined variable: my_variable \/system\/core\/Loader.php(829) : eval()'d code 84"
I know which file this is coming from, and it's a view. I'm aware of debug_backtrace and I'm thinking about making a string of it and concatenating it onto the end of the $msg variable in an overridden Log.php, but I first wanted to check two things with all you friends:
Is debug_backtrace the best way to do this? It returns a huge amount of data.
Is anyone aware of someone that's already done this? Seems like an obvious need for anyone using Codeigniter (...still ;)

Here's some code you could adapt to your needs (I recommend creating a My_Log and not editing the system file just in case CI does ever get an update). The reason I have this is complicated and not useful as-is any longer, but it might give you a start:
protected function log_query ($query, $type)
{
$backtrace = debug_backtrace ();
$backtrace = array_slice ( $backtrace, 2, -2 ); // first and last two elements are things that never change
$traced = array ();
foreach ( $backtrace as $bt )
{
$func = isset ( $bt['function'] ) ? $bt['function'].'()' : '';
$line = isset ( $bt['line'] ) ? $bt['line'] : '';
$file = isset ( $bt['file'] ) ? str_replace ( APP, '', $bt['file'] ) : '';
$object = isset ( $bt['object'] ) ? get_class ( $bt['object'] ) : '';
$args = array (); build_args ( $bt['args'], $args );
$args = implode ( '; ', $args );
if ( $object )
{
$obj_func = $object . '->' . $func;
} else {
$obj_func = $func;
}
$traced[] = "$file $obj_func $line $args";
unset($args);
}
$traced = implode ( "\n\t", $traced );
$date = MYSQL_DATE_TIME;
$uri = isset ( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '' ;
$query= preg_replace ( "/[\n\t]/", ' ', $query );
log_message ('error', "{$query}\n\n\t{$date} ({$type})\n\t{$uri}\n\t{$traced}\n--------------------------\n");
}

Related

php 7.2 upgrade error "Cannot assign an empty string to a string offset"

I'm working with an aged wordpress theme which I really like and I only have some basic coding skills. My provider forcably upgraded my server php version to 7.2 and of course some of my scripts are breaking down.
public function localize( $handle, $object_name, $l10n ) {
if ( $handle === 'jquery' )
$handle = 'jquery-core';
if ( is_array($l10n) && isset($l10n['l10n_print_after']) ) { // back compat, preserve the code in 'l10n_print_after' if present
$after = $l10n['l10n_print_after'];
unset($l10n['l10n_print_after']);
}
foreach ( (array) $l10n as $key => $value ) {
if ( !is_scalar($value) )
continue;
$l10n[$key] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8');
}``
According to the log the error is in the last line because in that like apparently it "Cannot assign an empty string to a string offset"
Maybe this is a lot more complicated than changing one simple thing....any solutions for that?
The most logical thing to do here is to change it to update in the in_array condition:
if ( is_array($l10n){
if(isset($l10n['l10n_print_after']) ) { // back compat, preserve the code in 'l10n_print_after' if present
$after = $l10n['l10n_print_after'];
unset($l10n['l10n_print_after']);
}
foreach ($l10n as $key => $value ) {
if ( !is_scalar($value) )
continue;
$l10n[$key] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8');
}
}
Even if you cast (array)$l10n to an array, this doesn't set the variable itself to be an array ( like $l10n = (array)$l10n).
That said, working with mixed types can be really cumbersome. It's better to send it only arrays or deal with the array bit first, that way you have a consistent type. Like this:
public function localize( $handle, $object_name, $l10n ) {
//normalize arguments
if(!is_array($l10n)) $l10n = [$l10n];
if ( $handle === 'jquery' )
$handle = 'jquery-core';
if ( isset($l10n['l10n_print_after']) ) { // back compat, preserve the code in 'l10n_print_after' if present
$after = $l10n['l10n_print_after'];
unset($l10n['l10n_print_after']);
}
foreach ($l10n as $key => $value ) {
if ( !is_scalar($value) )
continue;
$l10n[$key] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8');
}
}
Another possible route to fixing this is to force your server to run an older version of php if they can do that. For example with pantheon.io you can force the server to run a certain version of php in the server config file.
Unless you would like to modernize all your scripts, then ignore this.

php strpos Fatal error: Call to undefined function when using more than one if statement

To check for a products size i need to search through a simple string like: 32sdfsjkndgfjkdgndkj24020ldsfsfsd
To find the value inside the string i'm using strpos to look for the different sizes.
My current code is:
if (strpos($size, "1875cl") || strpos($size, "01875l")) {
$size = '18,75cl';
} elseif (strpos($size, "20cl") || strpos($size, "020l") || strpos($size, "02l")) {
$size = '20cl';
}
The problem is when i use an if statement.
The first if is working great. Then when the elseif is checking for another value inside the string - also with strpos the scripts returns a fatal error saying that the function is undefined:
Fatal error: Call to undefined function strpos() in .....
This works, same as yours will work, the only possible way it would break is that the data is overwritten or there is wrong data in the $size variable.
You can sanitize the $size variable more by using preg_replace( '/[^a-zA-Z0-9]/', '', $size ); and if you use the code below you can save the $correctedSize variable with the name.
$prodNames = array( '\x0D\x0D\x0D \n\rkajshdfsajkdhaksjd1875cladsas', '۞ ۩ εїз Ƹ̵̡Ӝ̵̨̄kajshdfsajkdhaksjd1875cladsas', 'kajshdfsajkdhaksjd20clcladsas','02lkajshdfsajkdhaksjdadsas', 'kajshdfsajkdhaksjd1875cladsas', 'kajshdfsajkdhaksjd1875cladsas', 'kajshdfsajkdhaksjd20clcladsas','02lkajshdfsajkdhaksjdadsas' );
foreach ( $prodNames as $prodNameRaw ) {
$correctedSize = 0;
$prodName = preg_replace( '/[^a-zA-Z0-9]/', '', $prodNameRaw );
if ( strstr( $prodName, '1875cl' ) || strstr( $prodName, '01875l' ) ) {
$correctedSize = '18,75cl';
} elseif ( strstr( $prodName, '20cl' ) || strstr( $prodName, '020l' ) || strstr( $prodName, '02l' ) ) {
$correctedSize = '20cl';
}
}

turning this trainwreck of a function into a recursive one

I've been trying to build this recursive function for the better part of a day now, but I just can't seem to get it to work the way I want.
First, I have a property which holds some data that the function have to access:
$this->data
And then I have this string which the intention is to turn into a relative path:
$path = 'path.to.%id%-%folder%.containing.%info%';
The part of the string that are like this: %value% will load some dynamic values found in the $this->data property (like so: $this->data['id']; or $this->data['folder'];
and to make things really interesting, the property can reference itself again like so: $this->data['folder'] = 'foldername.%subfolder%'; and also have two %values% separated by a - that would have to be left alone.
So to the problem, I've been trying to make a recursive function that will load the dynamic values from the data property, and then again if the new value contains another %value% and so on until no more %value%'s are loaded.
So far, this is what I've been able to come up with:
public function recursiveFolder( $folder, $pathArr = null )
{
$newPathArr = explode( '.', $folder );
if ( count ( $newPathArr ) !== 1 )
{
foreach( $newPathArr as $id => $folder )
{
$value = $this->recursiveFolder( $folder, $newPathArr );
$resultArr = explode( '.', $value );
if ( count ( $resultArr ) !== 1 )
{
foreach ( $resultArr as $nid => $result )
{
$nvalue = $this->recursiveFolder( $result, $newPathArr );
$resultArr[$nid] = $nvalue;
}
}
$resultArr = implode( '.',$resultArr );
$newPathArr[$id] = $resultArr;
}
}
else
{
$pattern = '/%(.*?)%/si';
preg_match_all( $pattern, $folder, $matches );
if ( empty( $matches[0] ) )
{
return $folder;
}
foreach ( $matches[1] as $mid => $match )
{
if ( isset( $this->data[$match] ) && $this->data[$match] != '' )
{
$folder = str_replace( $matches[0][$mid], $this->data[$match], $folder );
return $folder;
}
}
}
return $newPathArr;
}
Unfortunately it is not a recursive function at all as it grinds to a halt when it has multiple layers of %values%, but works with two layers -barely-. (I just coded it so that it would work at a bare minimalistic level this point).
Here's how it should work:
It should turn:
'files.%folder%.blog-%type%.and.%time%'
into:
'files.foldername.blog-post.and.2013.feb-12th.09'
based on this:
$data['folder'] = 'foldername';
$data['type'] = 'post';
$data['time'] = '%year%.%month%-%day%';
$data['year'] = 2013;
$data['month'] = 'feb';
$data['day'] = '12th.%hour%';
$data['hour'] = '09';
Hope you can help!
Jay
I don't see the need for this too be solved recursively:
<?php
function putData($str, $data)
{
// Repeat the replacing process until no more matches are found:
while (preg_match("/%(.*?)%/si", $str, $matches))
{
// Use $matches to make your replaces
}
return $str;
}
?>

Php regex returning repeats in nested arrays

I'm trying to get a list of all occurrences of a file being included in a php script.
I'm reading in the entire file, which contains this:
<?php
echo 'Hello there';
include 'some_functions.php';
echo 'Trying to find some includes.';
include 'include_me.php';
echo 'Testtest.';
?>
Then, I run this code on that file:
if (preg_match_all ("/(include.*?;){1}/is", $this->file_contents, $matches))
{
print_r($matches);
}
When I run this match, I get the expected results... which are the two include sections, but I also get repeats of the exact same thing, or random chunks of the include statement. Here is an example of the output:
Array (
[0] => Array ( [0] => include 'some_functions.php'; [1] => include 'include_me.php'; )
[1] => Array ( [0] => include 'some_functions.php'; [1] => include 'include_me.php'; ) )
As you can see, it's nesting arrays with the same result multiple times. I need 1 item in the array for each include statement, no repeats, no nested arrays.
I'm having some trouble with these regular expressions, so some guidance would be nice. Thank you for your time.
what about this one
<?php
preg_match_all( "/include(_once)?\s*\(?\s*(\"|')(.*?)\.php(\"|')\s*\)?\s*;?/i", $this->file_contents, $matches );
// for file names
print_r( $matches[3] );
// for full lines
print_r( $matches[0] );
?>
if you want a better and clean way, then the only way is php's token_get_all
<?php
$tokens = token_get_all( $this->file_contents );
$files = array();
$index = 0;
$found = false;
foreach( $tokens as $token ) {
// in php 5.2+ Line numbers are returned in element 2
$token = ( is_string( $token ) ) ? array( -1, $token, 0 ) : $token;
switch( $token[0] ) {
case T_INCLUDE:
case T_INCLUDE_ONCE:
case T_REQUIRE:
case T_REQUIRE_ONCE:
$found = true;
if ( isset( $token[2] ) ) {
$index = $token[2];
}
$files[$index] = null;
break;
case T_COMMENT:
case T_DOC_COMMENT:
case T_WHITESPACE:
break;
default:
if ( $found && $token[1] === ";" ) {
$found = false;
if ( !isset( $token[2] ) ) {
$index++;
}
}
if ( $found ) {
if ( in_array( $token[1], array( "(", ")" ) ) ) {
continue;
}
if ( $found ) {
$files[$index] .= $token[1];
}
}
break;
}
}
// if your php version is above 5.2
// $files index will be line numbers
print_r( $files );
?>
Use get_included_files(), or the built-in tokenizer if the script is not included
I'm searching through a string of another files contents and not the
current file
Then your best bet is the tokenizer. Try this:
$scriptPath = '/full/path/to/your/script.php';
$tokens = token_get_all(file_get_contents($scriptPath));
$matches = array();
$incMode = null;
foreach($tokens as $token){
// ";" should end include stm.
if($incMode && ($token === ';')){
$matches[] = $incMode;
$incMode = array();
}
// keep track of the code if inside include statement
if($incMode){
$incMode[1] .= is_array($token) ? $token[1] : $token;
continue;
}
if(!is_array($token))
continue;
// start of include stm.
if(in_array($token[0], array(T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, T_REQUIRE_ONCE)))
$incMode = array(token_name($token[0]), '');
}
print_r($matches); // array(token name, code)
Please read, how works preg_match_all
First item in array - it return all text, which is in regular expression.
Next items in array - that's texts from regular expression (in parenthesises).
You should use $matches[1]

Understanding the Wordpress vulnerability

A vulnerability has recently been disclosed that affects WordPress 2.8.3 and allows the admin user to be locked out of their account by changing the password.
This post on Full Disclosure details the flaw, and includes relevant code snippets. The post mentions that 'You can abuse the password reset function, and bypass the first step and then reset the admin password by submiting an array to the $key variable.'
I'd be interested in someone familiar with PHP explaining the bug in more detail.
Those affected should update to a new 2.8.4 release which apparently fixes the flaw.
wp-login.php:
...[snip]....
line 186:
function reset_password($key) {
global $wpdb;
$key = preg_replace('/[^a-z0-9]/i', '', $key);
if ( empty( $key ) )
return new WP_Error('invalid_key', __('Invalid key'));
$user = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->users WHERE
user_activation_key = %s", $key));
if ( empty( $user ) )
return new WP_Error('invalid_key', __('Invalid key'));
...[snip]....
line 276:
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'login';
$errors = new WP_Error();
if ( isset($_GET['key']) )
$action = 'resetpass';
// validate action so as to default to the login screen
if ( !in_array($action, array('logout', 'lostpassword', 'retrievepassword',
'resetpass', 'rp', 'register', 'login')) && false ===
has_filter('login_form_' . $action) )
$action = 'login';
...[snip]....
line 370:
break;
case 'resetpass' :
case 'rp' :
$errors = reset_password($_GET['key']);
if ( ! is_wp_error($errors) ) {
wp_redirect('wp-login.php?checkemail=newpass');
exit();
}
wp_redirect('wp-login.php?action=lostpassword&error=invalidkey');
exit();
break;
...[snip ]...
So $key is an array in the querystring with a single empty string ['']
http://DOMAIN_NAME.TLD/wp-login.php?action=rp&key[]=
reset_password gets called with an array, and then preg_replace gets called:
//$key = ['']
$key = preg_replace('/[^a-z0-9]/i', '', $key);
//$key = [''] still
because preg_replace accepts either a string or an array of strings. It regex replaces nothing and returns the same array. $key is not empty (it's an array of an empty string) so this happens:
$user = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->users
WHERE user_activation_key = %s", $key));
Now from here, I need to go read the wordpress source for how prepare behaves...
More:
So prepare calls vsprintf which produces an empty string
$a = array('');
$b = array($a);
vsprintf("%s", $b);
//Does not produce anything
So the SQL is:
SELECT * FROM $wpdb->users WHERE user_activation_key = ''
Which will apparently match the admin user (and all users without activation_keys I suppose).
And that's how.
I have a related question on how to patch this vulnerability - line 190 on the wp-login.php should now look like this;
if ( empty( $key ) || is_array( $key ) )

Categories